题解:AcWing 1064 小国王

【题目来源】

AcWing:1064. 小国王 - AcWing题库

【题目描述】

\(n\times n\) 的棋盘上放 \(k\) 个国王,国王可攻击相邻的 \(8\) 个格子,求使它们无法互相攻击的方案总数。

【输入】

共一行,包含两个整数 \(n\)\(k\)

【输出】

共一行,表示方案总数,若不能够放置则输出 \(0\)

【输入样例】

3 2

【输出样例】

16

【算法标签】

状压DP#

【代码详解】

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

#define int long long  // 定义宏,将int替换为long long类型
const int N = 12, M = 1 << 10, K = 105;  // 定义常量:棋盘最大行数N,状态数M,国王数K
int n, k;  // 棋盘大小n*n,需要放置的国王数量k
vector<int> state;  // 存储所有合法状态(不相邻的国王放置方式)
int id[M], cnt[M];  // id记录状态对应的索引,cnt记录状态中的国王数量
vector<int> head[M];  // 存储每个状态可以转移到的其他状态
int f[N][K][M];  // DP数组,f[i][j][a]表示前i行放置j个国王且第i行状态为a的方案数

// 检查状态是否合法(没有相邻的国王)
bool check(int state) {
    for (int i = 0; i < n; i++)
        if ((state >> i & 1) && (state >> (i + 1) & 1)  // 检查是否有相邻的1
            return false;
    return true;
}

// 计算状态中1的个数(国王数量)
int count(int state) {
    int res = 0;
    for (int i = 0; i < n; i++) 
        res += state >> i & 1;
    return res;
}

signed main() {
    cin >> n >> k;  // 输入棋盘大小和国王数量

    // 预处理所有合法状态
    for (int i = 0; i < (1 << n); i++) {
        if (check(i)) {
            state.push_back(i);  // 存储合法状态
            id[i] = state.size() - 1;  // 记录状态对应的索引
            cnt[i] = count(i);  // 记录状态中的国王数量
        }
    }

    // 预处理状态之间的转移关系
    for (int i = 0; i < state.size(); i++) {
        for (int j = 0; j < state.size(); j++) {
            int a = state[i], b = state[j];
            // 检查状态a和b是否可以相邻(不冲突且合并后仍合法)
            if ((a & b) == 0 && check(a | b)) {
                head[i].push_back(j);  // 记录状态i可以转移到状态j
            }
        }
    }

    // 初始化DP数组:第0行放置0个国王且状态为0的方案数为1
    f[0][0][0] = 1;

    // 动态规划过程
    for (int i = 1; i <= n + 1; i++) {  // 遍历每一行(多处理一行方便统计结果)
        for (int j = 0; j <= k; j++) {  // 遍历可能的国王数量
            for (int a = 0; a < state.size(); a++) {  // 遍历当前行的状态
                for (int b : head[a]) {  // 遍历可以转移到状态a的上一行状态b
                    int c = cnt[state[a]];  // 当前行状态a的国王数量
                    if (j >= c) {
                        // 状态转移:前i行放置j个国王的方案数 += 前i-1行放置j-c个国王的方案数
                        f[i][j][a] += f[i - 1][j - c][b];
                    }
                }
            }
        }
    }

    // 输出结果:前n+1行放置k个国王且第n+1行状态为0(即第n行无限制)的方案数
    cout << f[n + 1][k][0] << endl;

    return 0;
}

【运行结果】

3 2
16
posted @ 2026-04-02 11:53  团爸讲算法  阅读(2)  评论(0)    收藏  举报