トピックの説明
n×n のチェス盤に k 人のキングを配置すると、キングは 8 つの隣接するグリッドを攻撃し、お互いの攻撃を防ぐ計画の合計数を見つけることができます。
入力フォーマット
合計 1 行に 2 つの整数 n と k が含まれます。
出力フォーマット
合計 1 行でソリューションの総数を示します。配置できない場合は 0 が出力されます。
データ範囲
1 ≤ n ≤ 10、0
≤ k ≤ n ^ 2
入力サンプル:
3 2
出力例:
16
トピックのアイデア
この質問は、複数のバックパックのアイデアと非常に似ています。dfs を使用するとタイムアウトになります。
これは dp のバイナリで最適化できます。
キングを表す 2 進数の 1 について考えてみましょう。
1. i 行目に 6 などの 1 が隣接することはできず、その基数は 110 です。これは質問の意味を満たしません。
2. 行 i と行 i-1 を比較します。質問の意味は、周囲の 8 ビット 1 0 と 0 1 または 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;
}