リトルキング【状態圧縮DP】

トピックの説明

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;
}

おすすめ

転載: blog.csdn.net/zhi6fui/article/details/128558482