题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上 左下 右上 右下 八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018 / 4 / 25)
输入
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出
所得的方案数
输入样例
3 2
输出样例
16
分析
这道题一看就是个妥妥的状压DP
状态设计成三维,第一维行数,第二维当前行状态,第三维当前已放国王数。
另外多说一句,做多了会发现,基本上来说,状压DP只要带上了数量限制就要专门开一维。
代码
#include<bits/stdc++.h>
using namespace std;
int n, k;
long long dp[50][1024][110];
int cnt[1024];
bool g[1024];
long long ans;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int main() {
n = read(), k = read();
for (int i = 0; i < (1 << n); i++) {
int temp = i;
while (temp) {
if (temp & 1) cnt[i]++; //算出每个状态所需棋子数
temp >>= 1;
}
}
//预处理出合法状态
for (int i = 0; i < (1 << n); i++) {
if (!((i << 1) & i)) {
g[i] = 1;
}
}
//初始化
for (int i = 0; i < (1 << n); i++) {
if (g[i] && cnt[i] <= k) {
dp[1][i][cnt[i]] = 1;
}
}
//DP
for (int i = 2; i <= n; i++) { //行数
for (int j = 0; j < (1 << n); j++) { //当前行状态
if (!g[j]) continue;
for (int l = 0; l < (1 << n); l++) { //上一行状态
if (!g[l]) continue; //判断合法
if (l & j) continue;
if ((l >> 1) & j) continue;
if ((l << 1) & j) continue;//三个可能被攻击的方向
for (int s = k; s >= cnt[j]; s--) {
dp[i][j][s] += dp[i - 1][l][s - cnt[j]];//状态转移
}
}
}
}
//统计答案
for (int i = 0; i < (1 << n); i++) {
ans += dp[n][i][k];
}
printf("%lld", ans);
return 0;
}