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
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/mrsheep/p/8243944.html