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;

}

你可能感兴趣的:(it)