题解: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
浙公网安备 33010602011771号