题目传送门
解题思路:
好难懂的状压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 }