1592:国王(信息学奥赛一本通)
题目描述
在 n×n的棋盘上放 k个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。
输入
只有一行,包含两个整数 n 和 k。
输出
每组数据一行为方案总数,若不能够放置则输出0。
样例输入
样例1
3 2
样例2
4 4
样例输出
样例1
16
样例2
79
提示信息
数据范围与提示:
对于全部数据, 1 ≤ n ≤ 10 , 0 ≤ k ≤ n 2 1≤n≤10,0≤k≤n^2 1≤n≤10,0≤k≤n2。
题解1(C++版本)
#include<cstdio>
using namespace std;
typedef long long LL;
int n, k;
int s[1<<12]; // 同一行的合法状态集
int num[1<<12]; // 每个合法状态包含的国王个数
int cnt; // 同一行的合法状态个数
LL dp[12][144][1<<12]; // f[i][j][a]表示前i行放了j个国王,第i行第a个状态的方案数
int main(){
scanf("%d%d", &n, &k);
// 预处理
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); // 累计每个合法状态包含的国王个数
}
}
}
dp[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]]; //第i行第a个状态的国王个数
if(j >= c && !(s[b]&s[a]) && !(s[b]&(s[a]>>1)) && !(s[b]&(s[a]<<1))){
dp[i][j][a] = dp[i][j][a] + dp[i - 1][j - c][b];
}
}
}
}
}
printf("%lld\n", dp[n + 1][k][0]);
return 0;
}