SGU223 - Little Kings(状态压缩DP)

题目大意

给定一个N*N(n<=10,k<=n*n)大小的棋盘,要求你在棋盘上放置k个国王,使得不会相互攻击,如果棋盘上某个格子放置了一个国王,那么与他相邻的八个格子都是他的攻击范围,问有多少种放置方案

题解

和炮兵阵地差不多,只不过这个题是统计方案,依然是逐行转移,如果某个格子(i,j)放置了国王,那么上一行j,j-1,j+1,这三个格子都是他的攻击范围,因此这三个格子是不能够放国王的,由于我们需要的答案是恰好放置k个国王的方案数,所以方程还需要加一维,记录放置的国王数。先预处理出一行的合法状态(用s数组记录)以及放置(sum数组记录)的国王个数方程表示为:dp[i][a][k]+=dp[i-1][b][k-sum[a]](s[a]&s[b]==0)

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
#define MAXN 11
typedef long long LL;
LL dp[MAXN][1<<MAXN][MAXN*10];
int s[1<<MAXN],cnt,sum[1<<MAXN];
int get_sum(int x)
{
    int ret=0;
    while(x)
    {
        ret+=(x&1);
        x>>=1;
    }
    return ret;
}
void pre_solve(int n)
{
    for(int i=0;i<(1<<n);i++)
        if(!(i&(i>>1)))
        {
            s[cnt]=i;
            sum[cnt++]=get_sum(i);
        }
}
bool check(int a,int b)
{
    if(a&b) return false;
    if((a>>1)&b) return false;
    if((a<<1)&b) return false;
    return true;
}
int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        cnt=0;
        pre_solve(n);
        for(int i=0;i<cnt;i++)
        {
            dp[1][i][sum[i]]=1;
        }
        for(int i=2;i<=n;i++)
            for(int a=0;a<cnt;a++)
                for(int b=0;b<cnt;b++)
                    for(int c=sum[a];c<=k;c++)
                        if(check(s[a],s[b]))
                            dp[i][a][c]+=dp[i-1][b][c-sum[a]];
        LL ans=0;
        for(int i=0;i<cnt;i++)
            ans+=dp[n][i][k];
        printf("%I64d\n",ans);
    }
    return 0;
}

posted on 2013-12-03 13:22  仗剑奔走天涯  阅读(459)  评论(0编辑  收藏  举报

导航