BZOJ 1087: [SCOI2005]互不侵犯King | 状压DP
题目:
http://www.lydsy.com/JudgeOnline/problem.php?id=1087
题解:
注意到 N 很小,所以我们若一行一行放入国王,则上一行国王的状态以及这一行国王的
状态我们都是能利用二进制数枚举出来,而由于国王只影响周围八格,这给我们利用位运算
判断两个状态是否矛盾带来了有利条件:
((x << 1) & y) == 0 且 ((x >> 1) & y) == 0) 且 (x & y == 0)
f [ i ][ j ][ S ] 表示前 i 行放入了 j 个国王且第 i 行摆放情况为 S 的方案数。
枚举下一行的状态进行转移即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define N 10 #define M 26 #define P 512 using namespace std; typedef long long ll; int n,m,t,g[P]; ll ans,f[N][P][M]; int main() { scanf("%d%d",&n,&m); if (m>25 || m>=n*n) puts("0"); else { t=1<<n;f[0][0][0]=1;g[0]=0; for (int i=1;i<t;i++) g[i]=g[i>>1]+(i&1); for (int i=1;i<=n;i++) for (int j=0;j<t;j++) if (g[j]<=m && !(j&j>>1)) for (int k=0;k<t;k++) if (g[k]<=m && !(k&k>>1) && !(k&j) && !(j&k>>1) && !(j&k<<1)) for (int l=g[j]+g[k];l<=m;l++) f[i][j][l]+=f[i-1][k][l-g[j]]; for (int i=0;i<t;i++) ans+=f[n][i][m]; printf("%lld\n",ans); } return 0; }