小国王【状态压缩DP】

题目描述

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

猜你喜欢

转载自blog.csdn.net/zhi6fui/article/details/128558482