洛谷 P1896:[SCOI2005] 互不侵犯 ← 状态压缩DP

​【题目来源】
https://www.luogu.com.cn/problem/P1896

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

【输入格式】
只有一行,包含两个数 N,K。

【输出格式】
所得的方案数。​​​​​​​

【输入样例】
3 2

【输出样例】
16

【数据范围】
对于全部数据,1≤N≤9,0≤K≤N×N。

【算法分析】
● dp[i][cur][j] = 前 i 行摆放完毕,第 i 行状态为 cur,总共放了 j 个国王的合法方案数目。
● 状态转移方程里的三重判断

if(cur&pre) continue;        // 上下对齐位置不能同时有国王
if(cur&(pre<<1)) continue;   // 右上 ↘ 左下斜向不能同时有国王
if(cur&(pre>>1)) continue;   // 左上 ↙ 右下斜向不能同时有国王

【算法代码】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
LL dp[10][1<<10][100];
int n,k,cnt[1<<10];
vector<int> v;

int main() {
    cin>>n>>k;
    for(int s=0; s<(1<<n); s++) {
        if(s &(s<<1)) continue;
        v.push_back(s);
        cnt[s]=__builtin_popcount(s);
    }

    for(int i=0; i<v.size(); i++) {
        int s=v[i];
        dp[0][s][cnt[s]]=1;
    }

    for(int i=1; i<n; i++) {
        for(int cur:v) {
            for(int pre:v) {
                if(cur&pre) continue;
                if(cur&(pre<<1)) continue;
                if(cur&(pre>>1)) continue;
                for(int j=cnt[cur]; j<=k; j++)
                    dp[i][cur][j]+=dp[i-1][pre][j-cnt[cur]];
            }
        }
    }

    LL ans=0;
    for(int s:v) ans+=dp[n-1][s][k];
    cout<<ans<<endl;

    return 0;
}

/*
in:3 2
out:16
*/



【参考文献】
https://www.luogu.com.cn/problem/solution/P1896
 

​

posted @ 2026-11-05 18:01  Triwa  阅读(9)  评论(0)    收藏  举报