[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 }
View Code

 

posted @ 2016-06-28 20:04  CtrlCV  阅读(378)  评论(0编辑  收藏  举报