洛谷P1896_状压dp

本弱第一篇状压dp(在看了题解的前提下做的)


在n*n个格子内放m个国王,要求国王的四周即周围8个格子不能再有国王,求不同的放置方案数
将任意一行的一个状态看作是n位的二进制数,1表示放了国王,0表示没有放
dp[i][j][k] 表示前i行第j个状态,已经放置了k个国王的方案数;
can[i]第i个状态的数字,num[i]第i个状态的1的个数;

#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;

int n, m;//n*n的格子放m个
LL dp[11][1111][111];//前i行,第j个状态,共有k个国王的方案数

int can[1111], num[1111];//满足条件的数,以及它的含1的个数
int tot;

int getnum(int x)
{
    int ret = 0;
    while(x) ret += (x&1), x>>=1;
    return num[tot] = ret;
}

int main()
{
    int i, j, k, l, x, y;
    scanf("%d %d", &n, &m);
    memset(dp, 0, sizeof(dp));
    int maxn = (1<<n)-1;
    tot = 0;
    for(i = 0; i <= maxn; ++i)
        if(!(i&(i<<1)))//若二进制没有连续的1
            can[++tot] = i, dp[1][tot][getnum(i)] = 1;
    for(i = 2; i <= n; ++i)//枚举行号
        for(j = 1; j <= tot; ++j)//枚举第i行的状态
    {
        x = can[j];
        for(k = 1; k <= tot; ++k)//枚举第i-1行的状态
        {
            y = can[k];
            if((x&y) || (x&(y<<1)) || (x&(y>>1))) continue;
            for(l = 0; l <= m; ++l) dp[i][j][num[j]+l]+=dp[i-1][k][l];//更新状态
        }
    }
    LL ans = 0;
    for(i = 1; i <= tot; ++i) ans += dp[n][i][m];
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jay__bryant/article/details/80273906