【题解】洛谷P1896互不侵犯[SCOI2005] 状压DP

题目链接
将任意一行的一个状态看作是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,k;//n*n放k个
const int N=2e3;
ll dp[20][N][110];
//dp[i][j][l]表示前i行状态为j放l个国王的总方案数
int num[N],can[N],tot;
int getnum(int x)//返回1的个数 
{
    int ret=0;
    while(x)
    {
        ret+=(x&1);
        x>>=1;
    }
    return num[tot]=ret;//注意这里的处理 
}
int check(int x)
{
    return x&(x<<1);
}
void init()//预处理状态 
{
    int maxn=(1<<n)-1;//方案数上限
    for(int i=0;i<maxn;i++)
    if(!check(i))
    {
        can[++tot]=i;
        dp[1][tot][getnum(i)]=1;
    } 
}
int main()
{
    int i,j,l,x,y,z;
    ll ans=0;
    scanf("%d%d",&n,&k);//大小、个数
    memset(dp,0,sizeof(dp));
    init();
    for(i=2;i<=n;i++)//枚举行 
    for(j=1;j<=tot;j++) //枚举第i行状态
    {
        x=can[j];//取出状态
        for(l=1;l<=tot;l++)//枚举i-1行状态
        {
            y=can[l];//取出状态
            if((x&y)||(x&(y<<1))||(x&(y>>1)))continue;//非法状态
            for(z=0;z<=k;z++)dp[i][j][num[j]+z]+=dp[i-1][l][z];//更新状态 
        } 
    } 
    for(i=1;i<=tot;i++)
    ans+=dp[n][i][k];
    printf("%lld\n",ans);
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_41958841/article/details/81637236
今日推荐