Problem 1087. – [SCOI2005]互不侵犯King
1087: [SCOI2005]互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
而且貌似是一道状压的经典题
我们定义 f[i][cnt][zip] 表示对于第i层,该层及之前的层一共放了 cnt 个 King ,然后第 i 层的状态是压缩成的情况的编号
状态转移方程也很好想,对于前一行的一种状态,如果我们可以转移到当前行的状态的话,有
f[i][cnt][now]+=f[i−1][cnt−size[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;
}