【BZOJ1087】【SCOI2005】互不侵犯King(状压dp)

本文介绍了一道经典的状压动态规划问题:在N×N的棋盘中放置K个国王,使得它们不会互相攻击,求解所有可能的摆放方案数量。文章详细解释了解题思路,并提供了一份完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。

Sample Input

3 2

Sample Output

16

题解:
题目好短,我喜欢。
是非常经典的,在棋盘上进行的状压dp。首先,两个王不能挨在一起,所以“11”型是不合法的。做一遍预处理,记录一行的合法情况,与两行的合法情况。用dp[i][j][k]表示放到了第i行,放了j个王,当前行状态为k的情况数,枚举状态转移就好了。记得答案要long long记录。
代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define M 600
using namespace std;
int n,m,state;
bool c1[M],c2[M][M];
ll dp[11][120][M],cnt[M]; 
void pre()
{
    for(int i=0;i<state;i++)
    {
        if(i&(i>>1)) continue;
        for(int j=i;j>0;j>>=1) cnt[i]+=(j&1);
        c1[i]=1;
    }
    for(int i=0;i<state;i++)
    {
        if(!c1[i]) continue;
        dp[1][cnt[i]][i]=1;
        for(int j=0;j<state;j++)
        {
            if(!c1[j] || (i&j) || (i&(j>>1)) || ((i>>1)&j)) continue;
            c2[i][j]=1;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    state=(1<<n);
    pre();
    for(int i=2;i<=n;i++)
    for(int j=0;j<state;j++)
    {
        if(!c1[j]) continue;
        for(int k=0;k<state;k++)
        {
            if(!c1[k] || !c2[j][k]) continue;
            for(int l=cnt[k];l<=m-cnt[j];l++)
            dp[i][l+cnt[j]][j]+=dp[i-1][l][k];
        }
    }
    ll ans=0;
    for(int i=0;i<state;i++) ans+=dp[n][m][i];
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值