洛谷 P1896 [SCOI2005]互不侵犯

题目传送门

解题思路:

好难懂的状压DP,注释在代码里

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 
 4 using namespace std;
 5 
 6 int n,k;
 7 int s[800000],cnt[80000],tot;
 8 //对于每一个独立的行,tot是所有合法状态数的和,s[]是这些状态分别是什么,cnt[]是当前这种状态能够放几个king 
 9 long long ans,f[11][1500][82];
10 //f[i][j][k]表示到第i行,状态为j,第i行及以前一共放了k个国王的方案数 
11 
12 inline void dfs() {
13     int t = (1 << n);//一行里合法和不合法的状态就这么多 
14     for(int i = 0;i <= t; i++) {
15         if(!((i << 1) & i)) {//判断是否有两个国王紧靠着,如果是,就说明不是合法状态 
16             s[++tot] = i;
17             int u = i;
18             while(u) {
19                 cnt[tot] += u % 2;
20                 u >>= 1;
21             }
22         }
23     }
24 }
25 
26 int main() {
27     scanf("%d%d",&n,&k);
28     dfs();//预处理第一行,以及推出以后每行的普遍规律 
29     for(int i = 1;i <= tot; i++)//处理第一行 
30         if(cnt[i] <= k)//第一行如果放的国王数不大于总数,则说明这是一个合法的答案 
31             f[1][i][cnt[i]] = 1;
32     for(int i = 2;i <= n; i++)//当前到第几行 
33         for(int j = 1;j <= tot; j++)//当前行的状态 
34             for(int r = 1;r <= tot; r++) {//上一行的状态 
35                 if(s[j] & s[r]) continue;
36                 if((s[j] << 1) & s[r]) continue;
37                 if((s[r] << 1) & s[j]) continue;
38                 //以上三行表示这一行的国王与前一行冲突,则不合法 
39                 for(int a = 1;a <= k; a++) {//枚举本行以前一共放了几个国王 
40                     if(a + cnt[j] > k) continue;//大于总数,不合法 
41                     f[i][j][a+cnt[j]] += f[i-1][r][a];//状态转移 
42                 }
43             }
44     for(int i = 1;i <= n; i++)
45         for(int j = 1;j <= tot; j++)
46             ans += f[i][j][k];            
47     printf("%lld",ans);
48     return 0;
49 }
原文地址:https://www.cnblogs.com/lipeiyi520/p/11826971.html