[BZOJ1087] [SCOI2005]互不侵犯King

题目描述

Description

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

Input

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

Output

方案数。

Sample Input

3 2

Sample Output

16

题目分析:

状压DP dp[i][j][k]代表第i行 数量为j时 状态为k 的方案数

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
bool map[110][110];
int check(int x)
{
    if(x&(x<<1)||x&(x>>1))
        return 0;
    int cnt=0;
    while(x)
    {
        x=x&(x-1);
        cnt++;
    }
    if(cnt>m) return 0;
    return 1;
}
int getnum(int x)
{
    int cnt=0;
    while(x)
    {
        x=x&(x-1);
        cnt++;
    }
    return cnt;
}
int num[110];
int stay[110];
long long dp[10][110][110];
long long ans;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<(1<<n);i++)
        if(check(i))
            num[++num[0]]=getnum(i),stay[++stay[0]]=i;
    for(int i=1;i<=num[0];i++)
        for(int j=1;j<=num[0];j++)
            if(!(stay[i]&stay[j])&&!((stay[i]>>1)&stay[j])&&!((stay[i]<<1)&stay[j]))
                map[i][j]=true;
    for(int i=1;i<=num[0];i++)
        dp[1][num[i]][i]=1;
    for(int i=2;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=1;k<=num[0];k++)
            {
                if(num[k]>j) continue;
                for(int l=1;l<=num[0];l++)
                    if(map[k][l]&&num[k]+num[l]<=j)
                        dp[i][j][k]+=dp[i-1][j-num[k]][l];
            }
    for (int i=1;i<=num[0];i++) ans+=dp[n][m][i];
    printf("%lld",ans);
    return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注