bzoj 1087 [SCOI2005] 互不侵犯King

Posted ck666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1087 [SCOI2005] 互不侵犯King相关的知识,希望对你有一定的参考价值。

1087: [SCOI2005]互不侵犯King

2017-08-24

 

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。


Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)


Output

  方案数。


Sample Input

3 2

Sample Output

16
数据n很小,(1<=n<=9)这样就很容易就想到暴力dfs,在方块中枚举每一个king在哪里(但当k大约到50时大约就是50!的计算量,不T才怪qwq);
这样就换一个思路的说?
对于每一个位置,都是可以用1|0表示(0表示没有选这个位置,1表示选(有king))
这样能把一行的信息压缩成一个数x;这就是所谓的状压dp
枚举每一行的信息然后转移,然后就过了的说
然后c[i]表示i这个数是否自己和本身发生冲突
c2[i][j]表示i与j不发生冲突->具体就是i和j,j和i左移一位,i和j左移一位不冲突(所有按位与运算等于0)
cnt记录这个数用了几个1;
其实c[i]只是对循环进行优化而已,就是这样的说
技术分享
#include<iostream>
using namespace std;
const int maxn=1<<11;
int n,k,cnt[maxn];
bool c[maxn],c2[maxn][maxn];
int tot;
long long ans,dp[10][100][maxn];
void df(){
    for(int i=0;i<=tot;i++){
        int s=0;
        if((i&(i>>1))==0){
            for(int j=i;j;j=j>>1)s+=(j&1);
            c[i]=1;cnt[i]=s;
        }
    }
    for(int i=0;i<=tot;i++)if(c[i])
        for(int j=0;j<=tot;j++)if(c[j])
            if(((i&j)==0)&&(((j>>1)&i)==0)&&(((i>>1)&j)==0))
            c2[i][j]=1;
}
int main(){
    cin>>n>>k;tot=(1<<n)-1;df();
    for(int i=0;i<=tot;i++)if(c[i])dp[1][cnt[i]][i]=1;
    for(int i=1;i<n;i++)
        for(int j=0;j<=tot;j++)if(c[j])
            for(int l=0;l<=tot;l++)if(c[l])
            if(c2[j][l])
                for(int p=0;p<=k;p++)
                    dp[i+1][cnt[l]+p][l]+=dp[i][p][j];
    for(int i=0;i<=tot;i++)ans+=dp[n][k][i];
    cout<<ans;
    return 0;
}
1084(s_a_b_e_r)

by:s_a_b_e_r


 

以上是非常正经的s_a_b_e_r的题解
以下是非常不正经的wypx的题解

……

不过感觉他说的挺详细了就没我什么事了吧╮(╯▽╰)╭

做的时候没用什么c1[i]啊c2[i][j]啊什么的,感觉好麻烦x

在dp之前先dfs一遍

把所有可能的状态都压进一个vector里

然后dp的时候就不用枚举所有状态了,直接从vector里往外拿

f[i][j][x]表示处理到第i行,放了j个棋子,现在的状态编号是x

(其实他不用vector是因为他不会用x)

技术分享
#include<iostream>
#include<cstdio>
#include<vector>

using namespace std;
const int N=200;
int n,k,num[N];
bool d[10];
long long f[N][N][N];
vector<int>a;
void dfs(int x) 
{
    if(x>n)
    {
      int ans=0;
      for(int i=1;i<=n;++i)
      {
        ans<<=1;
        if(d[i]){++ans;++num[a.size()];}
      }
      a.push_back(ans);
      return;
    }
    dfs(x+1);
    if(!d[x-1])
    {
      d[x]=1;
      dfs(x+1);
      d[x]=0;
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    dfs(1);
    int tot=a.size();
    for(int i=0;i<tot;++i)
      f[1][num[i]][i]=1;
    for(int i=1;i<n;++i)
    for(int x=0;x<tot;++x)
    for(int y=0;y<tot;++y)
      if( ((a[x]&a[y])==0) && (((a[x]<<1)&a[y])==0) && ((a[x]&(a[y]<<1))==0) )
        for(int j=num[x];j+num[y]<=k;++j)
          f[i+1][j+num[y]][y]+=f[i][j][x];
    long long ans=0;
    for(int i=0;i<tot;++i)ans+=f[n][k][i];
    cout<<ans<<endl;
    return 0;
}
1084(wypx)

by:wypx


 

 s:今天的天空璋好花啊,音乐棒棒的x

w:快去做题>_<



以上是关于bzoj 1087 [SCOI2005] 互不侵犯King的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1087: [SCOI2005]互不侵犯King

BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

BZOJ 1087 [SCOI2005]互不侵犯King (状压DP)

BZOJ 1087 [SCOI2005]互不侵犯King

bzoj 1087 [SCOI2005] 互不侵犯King

bzoj 1087: [SCOI2005]互不侵犯King