首页 > 技术文章 > 互不侵犯【状压】

hzoi-poozhai 2020-04-16 11:44 原文

互不侵犯【状压】

description

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

(1 <= N <= 9, 0 <= K <= N x N)

input

只有一行,包含两个数 n, k。

output

所得的方案数。

Sample

3 2
16

题解

大家都做过玉米地吧,这其实和那道题很像,只是玉米地是上下左右冲突,这个多了个角上也冲突,那就再加两个if判断角上冲突的情况

具体流程详见注释

#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
int dp[10][1<<10][100];//dp[i][j][k]表示第i行摆放的国王状态为j,总共摆放k个国王的方案数
int state[1<<10], num[1<<10];//预处理每行所有合法的状态和他们包含1的个数
int n, cnt, tot, ans;//tot为每行合法状态总数
int lowbit(int x){return x & -x;}
int getnum(int x){//求状态x中1的个数
    int ans = 0;
    for(int i=1; i<=n; i++) if(x & (1<<(i-1))) ans ++;
    return ans;
}
signed main(){
    scanf("%lld%lld", &n ,&cnt);
    for(int i=0; i<(1<<n); i++) if(!(i & (i<<1))) state[++tot] = i, num[tot] = getnum(i);//预处理合法状态
    for(int i=1; i<=tot; i++) dp[1][i][num[i]] = 1;//第一行特殊处理
    for(int i=2; i<=n; i++){
        for(int j=1; j<=tot; j++){//枚举当前行的状态
            for(int k=1; k<=tot; k++){//枚举上一行的状态
                if(state[j] & state[k]) continue;//上下相邻,相互攻击
                if(state[j] & (state[k]<<1)) continue;//攻击左上
                if(state[j] & (state[k]>>1)) continue;//攻击右上
                for(int now=cnt; now>=num[j]; now--){//状态合法,此时我们枚举当前棋盘上已经放的国王数量
                    dp[i][j][now] += dp[i-1][k][now - num[j]];
                }
            }
        }
    }
    for(int i=1; i<=tot; i++) ans += dp[n][i][cnt]; //枚举最后一行的方案,求和方案数
    printf("%lld\n", ans);
    return 0;
}

推荐阅读