BZOJ 1087: [SCOI2005]互不侵犯King 状态压缩 题解

来源:互联网 发布:java 两个类互相引用 编辑:程序博客网 时间:2024/06/03 14:55

Problem 1087. – [SCOI2005]互不侵犯King

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4028  Solved: 2338
[Submit][Status][Discuss]

Description

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

左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

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

Output

  方案数。

Sample Input


3 2

Sample Output


16

HINT


Source


[Submit][Status][Discuss]


HOME
Back








而且貌似是一道状压的经典题
我们定义f[i][cnt][zip]表示对于第i层,该层及之前的层一共放了cntKing,然后第i层的状态是压缩成的情况的编号zip,显然这道题最后的答案是所有f[n][k][]的和,即表示第n层及以前的层,放了kKing,最后一层各种不同的放法的放法总和
状态转移方程也很好想,对于前一行的一种状态,如果我们可以转移到当前行的状态的话,有
f[i][cnt][now]+=f[i1][cntsize[now]][last]

其中now表示这一行的状态,last表示上一行的状态,size[now]表示这一行的这一种放法放了多少个King在这一行,如果可以转移的话,自然有这样的转移方程
我们还需要考虑如何预处理出每一行可以放的状态以及一种状态是否也可以转移到另一种状态,这个看代码应该就可以看懂了
#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>#include<vector>#include<map>#include<set>const int MAXN=100+10;const int MINN=10+1;using namespace std;int readin(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9')ch=getchar();    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}bool judge[MAXN][MAXN];int zip[MAXN],siz[MAXN],tail;long long f[MINN][MAXN][MAXN];int m,k;void dfs(int num,int pos,int val){    zip[++tail]=val;    siz[tail]=num;    if(num>=k||num>=((m+1)>>1)) return;    for(register int i=pos+2;i<=m;i++) dfs(num+1,i,val+(1<<(i-1)));}int main(){    //freopen(".txt","r",stdin);    //freopen(".out","w",stdout);    m=readin();k=readin();    dfs(0,-1,0);    for(register int i=1;i<=tail;i++){        for(register int j=1;j<=tail;j++){            if(zip[i]&zip[j]||(zip[i]<<1)&zip[j]||(zip[i]>>1)&zip[j]) judge[i][j]=judge[j][i]=false;            else judge[i][j]=judge[j][i]=true;         }    }    for(register int i=1;i<=tail;i++) f[1][siz[i]][i]=1ll;    for(register int i=2;i<=m;i++){        for(register int j=0;j<=k;j++){            for(register int last=1;last<=tail;last++){                if (siz[last]>j) continue;                for(register int now=1;now<=tail;now++){                    if(judge[last][now]&&siz[last]+siz[now]<=j) f[i][j][now]+=f[i-1][j-siz[now]][last];                }            }        }    }    long long final=0;    for(register int i=1;i<=tail;i++) final+=f[m][k][i];    printf("%lld\n",final);    return 0;}

这里写图片描述