题目描述
在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的8个格子,求使它们无法互相攻击的方案总数。
输入格式
共一行,包含两个整数 n 和 k。
输出格式
共一行,表示方案总数,若不能够放置则输出0。
数据范围
1 ≤ n ≤ 10,
0 ≤ k ≤ n ^ 2
输入样例:
3 2
输出样例:
16
题目思路
这一道题和多重背包的思想很相似。在用dfs会超时。
这是可以用dp的二进制进行优化。
考虑二进制的1代表一个国王。
1、第i行的中不能有相邻的1出现,如6 而进制为 110 不符合题意。
2、第i行和第i-1行相比,因为题意说了周围8位 1 0 和 0 1 or 01 和 1 0 这种情况是不符合题意的。
考虑到这里这题基本就解决了。
解题代码:
#include<bits/stdc++.h>
using namespace std;
int cnt = 0;
int s[1 << 12];
int num[1 << 12];
long long f[12][144][1 << 12];//前 i行 有 多少个国王 当在i + 1行时的国王个数是否和前面的第i行相容
int main() {
int n, k;
cin >> n >> k;//n行 k个国王
//预处理 防止相邻的国王在一起 用二进制表示的话 如 i = 6 110 不符合要求
for (int i = 0; i < (1 << n); i++) {
if (!(i & i >> 1)) {
s[cnt++] = i;
for (int j = 0; j < n; j++) {
num[i] += (i >> j & 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 < cnt; a++) { // 第i行
for (int b = 0; b < cnt; b++) { //i - 1行
int c = num[s[a]];
if ((j >= c) && !(s[a] & s[b]) && !(s[b] & (s[a] << 1)) && !(s[b] & (s[a] >> 1))) {
f[i][j][a] += f[i-1][j - c][b];
}
}
}
}
}
cout << f[n + 1][k][0]; //这样子处理避免了还要累加的情况 在f在直接进行了
return 0;
}