题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
——by洛谷
https://www.luogu.org/problem/show?pid=1896
显然的棋盘模型的状压DP,故考虑对每行二进制状压,状压为表示有无王的01串,再由题意得到如下的限制:
- 每个串中不存在连续两个以上的1
- 相邻两串的同列不存在同为1的情况
- 不存在与上一行左侧或右侧同为1的情况
于是对于限制一,可在预处理中实现;对于二三,可在转移时限制,得出状态转移方程:
f[i][j][l+c[j]]=Σf[i-1][k][l];(i:当前行数;j:枚举本行所有可能状态,l+c[j]:此时的王的个数,c[j]:状态s[j]中王的个数)
然后对k进行限制,s[j]&s[k]==0,s[j]&(s[k]<<1)==0,s[j]&s[k]>>1)==0;之类的
需要注意的是:
- dfs预处理
- 处理好c[j]
总结:简单的DP么
代码如下:
#include<cstdio> using namespace std; long long f[10][1000][100]; int s[100],c[100]; int man; int n,m; void dfs(int ,int ,int ,int ); int main() { int i,j,k,l; long long ans=0; scanf("%d%d",&n,&m); dfs(1,0,0,0); f[0][1][0]=1; for(i=1;i<=n;i++) for(j=1;j<=man;j++) for(k=1;k<=man;k++) if((s[j]&s[k])==0&&(s[j]&(s[k]<<1))==0&&(s[j]&(s[k]>>1))==0) for(l=c[k];l<=m-c[j];l++) f[i][j][l+c[j]]+=f[i-1][k][l]; for(i=1;i<=man;i++) ans+=f[n][i][m]; printf("%lld",ans); return 0; } void dfs(int now,int num,int ma,int p) { int i; if(ma>m)return; if(now==n+1) { s[++man]=num; c[man]=ma; return; } for(i=0;i<=1;i++) if(1-p>=i) dfs(now+1,num+i*(1<<(now-1)),ma+i,i); }
祝AC哟;