题面
在 $N*N$ 的棋盘上放 $K$ 个国王,国王可攻击相邻的 $8$ 个格子,求使它们无法互相攻击的方案总数。
思路
状压 DP 的模板,以前 DP 技能一直没怎么点,现在数据结构什么的都差不多了,开始补 DP。
预处理
对于每一行来说,我们可以用二进制来表示当前这一行的放置状态。然后再压成十进制。
然后预处理出对于一行来说的所有状态(dfs)。
用 $s[i]$ 表示对于一行的第 i 种状态, $t[i]$ 表示这种状态用掉多少个棋子。
用 $f[i][j][k]$ 表示第 i 行,状态为 j,总共(包括之前)用了 k 个棋子的方法数。
然后用位运算来判断前后两行是否合法,转移就好了。
代码
#include<bits/stdc++.h> using namespace std; long long n,K,cnt=0,s[100000],t[100000],f[20][200][200],ans=0; void dfs(int now,int sum,int pos){ if (pos>=n){ s[++cnt]=now; t[cnt]=sum; return; } dfs(now,sum,pos+1); dfs(now+(1<<pos),sum+1,pos+2); } int main(){ cin>>n>>K; dfs(0,0,0); for (int i=1;i<=cnt;i++){ f[1][i][t[i]]=1; } for (int i=2;i<=n;i++){ for (int j=1;j<=cnt;j++){ for (int k=1;k<=cnt;k++){ if ((s[j]&s[k])||((s[j]<<1)&s[k])||((s[j]>>1)&s[k])){ continue; } for (int x=K;x>=t[j];x--){ f[i][j][x]+=f[i-1][k][x-t[j]]; } } } } for (int i=1;i<=cnt;i++){ ans+=f[n][i][K]; } cout<<ans; }