bzoj1087:[SCOI2005]互不侵犯King
Submit: 2358 Solved: 1380
[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
题解:
状压DP的特征还是很显然的,把每一行放国王表示成1,不放国王表示成0,由此可以得到一个01串,此01串所对应的十进制数就是压缩后的状态。
显然对于每一行的情况只由上一行决定,因此满足DP的无后效性。
f[i][k][p]表示第i行用情况k,第i-1行用情况p,state[i]表示情况i所对应的十进制数,cost[i]表示情况i所耗费的国王数。
因为N<=9,所以对于情况数而言不会超过2^9个,但明显其中肯定有某两个国王相邻的,这样本行就矛盾了,何谈与上一行进行比较?由此我们可以先预处理出所有情况,放在state[]和cost[]中,状态转移方程见下面的代码。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 LL N,K; 5 LL cnt; 6 LL state[1<<10+10],cost[1<<10+10]; 7 LL f[11][1<<10][110];//f[i][j][k]//第i行用情况j,总共用了k个国王 8 LL ANS; 9 int main(){ 10 scanf("%lld%lld",&N,&K); 11 for(LL i=0;i<=(1<<N)-1;i++){ 12 LL tmp=i; 13 if( (((i<<1)&i)) || ((i>>1)&i) ) continue; 14 cost[++cnt]=tmp%2; 15 while(tmp=(tmp>>1)) cost[cnt]+=tmp%2; 16 if(cost[cnt]<=K) state[cnt]=i; 17 else cost[cnt--]=0; 18 } 19 for(LL i=1;i<=cnt;i++){ 20 f[1][i][cost[i]]=1; 21 } 22 for(LL i=2;i<=N;i++){//枚举2~N行 23 for(LL j=1;j<=cnt;j++){//枚举第i行的状态 24 for(LL p=1;p<=cnt;p++){//枚举第i-1行的状态 25 if(((state[j]<<1)&state[p])||((state[j]>>1)&state[p])||(state[j]&state[p])) continue;//不会有相互吃的情况 26 for(LL k1=0;k1<=K;k1++){//枚举第i-1行及以前用的国王数 27 if(k1+cost[j]<=K) f[i][j][k1+cost[j]]+=f[i-1][p][k1]; 28 } 29 } 30 } 31 } 32 for(LL i=1;i<=cnt;i++){ 33 ANS+=f[N][i][K]; 34 } 35 cout<<ANS; 36 return 0; 37 }