Submit: 5528 Solved: 3237
[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
刚开始看上去很像八皇后问题,爆搜?实际上写了一遍爆搜发现TLE了,仔细分析可以发现状态实在是太多了,于是开始翻题解,发现是动态规划
状压DP,以二进制压缩状态
首先给出动规方程:
[f[i][j][now]=sum _{last=0}^{(1<<n)-1}f[i-1][j-num[now]][last]]
其中最大的难点是怎么判断国王位置合法,这就要用到神奇的二进制位运算了,想起了著名的约瑟夫问题,也是利用二进制得出答案
对于当前行,设当前状态为now,!now&(now<<1),如果成立,则当前行合法。因为这就排除了两个国王相邻的可能性
对于上下相邻的两行,用类似算法,!(now&(last<<1)) && !((now<<1)&last) && !(now&last)
是不是看的眼睛有点花,需要仔细分析
为了代码简洁,实际使用continue进行排除处理
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 #define LL long long 6 7 const int MAXN=1<<9; 8 9 LL n,k,ans; 10 LL num[MAXN],f[10][82][MAXN]; 11 12 void init() 13 { 14 for(int i=0;i<(1<<n);i++) 15 if(!(i&(i<<1))) 16 { 17 int t=i; 18 while(t) 19 { 20 num[i]+=t&1; 21 t>>=1; 22 } 23 f[1][num[i]][i]=1; 24 } 25 } 26 27 int main() 28 { 29 cin>>n>>k; 30 init(); 31 for(int i=2;i<=n;i++) 32 for(int j=0;j<=k;j++) 33 for(int now=0;now<(1<<n);now++) 34 { 35 if((now&(now<<1))||j<num[now]) continue; 36 for(int last=0;last<(1<<n);last++) 37 { 38 if(now&(last<<1)||((now<<1)&last)||(now&last)) continue; 39 f[i][j][now]+=f[i-1][j-num[now]][last]; 40 } 41 } 42 for(int i=0;i<(1<<n);i++) 43 ans+=f[n][k][i]; 44 cout<<ans; 45 return 0; 46 }