【SCOI2005】互不侵犯

题面

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

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

 

分析

数据规模极小+状态极多+网格题 == 状压dp

套路:有放置的个数限制我们就需要在dp加一维。

dp[i][j][k]表示扫到第i层,本层选择j状态,到目前为止总共用了k个国王的方案数

还是需要预处理第一行。额外的,还需要预处理出每一种状态需用的国王数量(即计算二进制位有多少个1)

#include<bits/stdc++.h>
using namespace std;
#define N 10
#define M 1<<10
#define ll long long
ll dp[N][M][N*N],ok[M],need[M];
ll n,k,mx,ans;

int main()
{
    cin>>n>>k;
    mx=(1<<n)-1;
    
    for(int i=0;i<=mx;i++)
    {
        int pos=i;
        while(pos)
        {
            if(pos%2)need[i]++;
            pos>>=1;//计算出每种状态需要多少King 
        }
    }
    
    for(int i=0;i<=mx;i++)
        if( ((i>>1)&i)==0 )
            ok[i]=1; 
    
    for(int i=0;i<=mx;i++)
        if( ok[i] & need[i]<=k)
            dp[1][i][need[i]]=1;
        
    for(int i=2;i<=n;i++)
        for(int j=0;j<=mx;j++)
            if(ok[j])
                for(int s=0;s<=mx;s++)
                {
                    if(s&j)continue;
                    if((s<<1)&j)continue;
                    if((s>>1)&j)continue;
                    for(int t=k;t>=need[j];t--)
                        dp[i][j][t]+=dp[i-1][s][t-need[j]];
                }
    
    for(int i=0;i<=mx;i++)
        ans+=dp[n][i][k];
    cout<<ans;
}

 

posted @ 2018-08-22 21:22  WJEMail  阅读(222)  评论(0编辑  收藏  举报