HDU 2553 N皇后问题(DFS剪枝、多组样例打表)

题目链接:点击这里

HDU 2553 N皇后问题(DFS剪枝、多组样例打表)_第1张图片

例如,下图是 n = 5 n = 5 n=5 的情况,其中下图左侧是一个合法的方案,而右侧由于有两个皇后在同一条对角线上,因此不是合法的方案。

HDU 2553 N皇后问题(DFS剪枝、多组样例打表)_第2张图片

对于这个问题,如果采用组合数的方式来枚举每一种情况 ( 即从 n 2 n^2 n2 个位置中选择 n n n 个位置),那么将需要 C n ∗ n n C_{n*n}^n Cnnn 的枚举量,当 n = 8 n = 8 n=8 时就是 54502232 54502232 54502232 次枚举,如果 n n n 更大,那么就会无法承受。

但是换个思路,考虑到每行只能放置一个皇后、每列也只能放置一个皇后,那么,从小到大固定好行号,把列号依次写出来,就会得到 1 ∼ n 1 \sim n 1n 的一个排列。例如,对上图左侧来说对应的排列是 31425 31425 31425,对上图右侧来说就是 35142 35142 35142

于是,就只需要枚举 1 ∼ n 1 \sim n 1n 的所有排列,查看每个排列对应的放置方案是否合法,统计其中合法的方案即可。由于总共有 n ! n! n! 个排列,因此当 n = 8 n = 8 n=8 时只需要 40320 40320 40320 次枚举,比之前的做法优秀许多。

剪枝:当已经放置了一部分皇后时,可能剩余的皇后无论怎样放置都不可能合法,此时就没必要往下递归了,直接返回上层即可,这样可以减少很多计算量。例如上图右侧,当放置了前三个皇后(对应生成了排列的一部分,即 351 351 351),可以发现剩下两个皇后不管怎么放置都会产生冲突,就没必要继续递归了。

注意:这题多组测试样例,需要将答案打表存储到数组中。 N ≤ 10 N≤10 N10 范围较小,可能会有重复测试。如果不打表,而是等输入 N N N 后再单独计算输出,会超时。

#include
#include
#include
#include
#include

using namespace std;

int n, cnt;
bool st[30];
int p[30];      // 排列
int ans[30];	// 打表 

void dfs(int idx)                         // idx是行号
{
	if(idx == n + 1)                      // 递归边界,即到达了第n+1行
	{
		cnt++;                            // 能到达这里的一定合法 
		return;
	}
	
	for(int i = 1; i <= n; i++)            // 有1~n列可选
	{
		if(st[i] == false)                 // 如果第i列没有放皇后
		{
			bool flag = true;               // 则判断(idx,i)与之前的皇后(j,p[j])是否冲突
			for(int j = 1; j < idx; j++)
			{
				if(abs(idx - j) == abs(i - p[j]))
				{
					flag = false;	        // 与之前的皇后在一条对角线,冲突 
					break;
				}
			}

			if(flag)
			{
				p[idx] = i;	            // 令第idx行皇后的列号为i 
			    st[i] = true;	        // 第i列被占用
				dfs(idx + 1);		    // 递归处理第idx + 1行 
				st[i] = false;	        // 恢复现场
			}
		}
	}
}

int main()
{
	while(~scanf("%d", &n) && n)
	{
		if(ans[n])
		{
			printf("%d\n", ans[n]);
			continue;
		}
		
		cnt = 0;
		memset(st, false, sizeof st);
		
		dfs(1);
		
		printf("%d\n", cnt);
		ans[n] = cnt;
	}
	
	return 0;
}

你可能感兴趣的:(DFS及其剪枝)