[BZOJ1087] [SCOI2005] 互不侵犯King (状压dp)
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
Solution
状压$dp$就是把状态压缩成二进制数,利用二进制的位运算改进算法的一种方法
这道题就把单行每个格子是否放国王当成状态,这样每一行就是一个不超过$2^n$的数,然后就可以光明正大地用按位与运算判断是否攻击
可以预处理单行所有合法状态,就不用每个二进制数枚举了。实际验证其合法方案数就是$Fibonacci_{n+2}$,比$2^n$小很多
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll f[2][100][600], cnt[600], a[600]; 5 6 bool check(int j, int k) 7 { 8 if(j & k << 1 || j & k || j & k >> 1) return false; 9 return true; 10 } 11 12 int main() 13 { 14 int n, m, tot = 0; 15 ll (*f0)[600] = f[0], (*f1)[600] = f[1], ans = 0; 16 cin >> n >> m; 17 f1[0][0] = 1; 18 for(int i = 0; i < 1 << n; ++i) 19 { 20 for(int j = 0; j < 9; ++j) 21 if(i & 1 << j) ++cnt[i]; 22 if(!(i & i << 1) && !(i & i >> 1)) 23 a[++tot] = i; 24 } 25 for(int i = 1; i <= n; ++i) 26 { 27 swap(f0, f1); 28 memset(f1, 0, 480000); 29 for(int j = 1; j <= tot; ++j) 30 for(int k = 1; k <= tot; ++k) 31 for(int l = cnt[a[j]]; l <= (i - 1) * n; ++l) 32 if(check(a[j], a[k])) 33 f1[l + cnt[a[k]]][a[k]] += f0[l][a[j]]; 34 } 35 for(int i = 1; i <= tot; ++i) 36 ans += f1[m][a[i]]; 37 cout << ans << endl; 38 return 0; 39 }