N皇后问题

解题思路

  由于N皇后问题要求每一行、列均有一个皇后,且任意两个均不在同一对角线上,故可对每一列中皇后所在行进行枚举,如下:

八皇后图
  如上图中,对1~8进行全排列,淘汰掉处于同一对角线的情况,上图枚举结果为{4,2,8,5,7,1,3,6},即(列,行){(1,4), (2,2), (3,8), (4,5), (5,7), (6,1), (7,3), (8,6)}。

1. 暴力法

  当达到递归边界时,表示生成一个结果,遍历每两个皇后,判断它们而是否在同一条对角线上。其中abs()函数为绝对值函数,存在于“stdlib.h”头文件中。
代码如下:

#include<cstdio>
#include<cstdlib> 
const int maxn = 11;
//P为当前排列,hashTable记录整数x是否已经在P中
int n, P[maxn], hashTable[maxn] = {false};
int count = 0;

/*---当前处理排列的第index号位---*/
void generateP(int index){
	if(index == n + 1){  //递归边界,生成一个排列 
		bool flag = true;  //flag为true表示当前排列为一个合法方案 
		for(int i = 1; i <= n; i++){  //遍历任意两个皇后 
			for(int j = i + 1; j <= n; j++){
				if(abs(i - j) == abs(P[i] - P[j])){  //如果在一条对角线上,其中abs()函数为绝对值函数 
					flag = false;
				}
			}
		} 
		if(flag)  count++;  //若当前方案合法,令count加1 
		return;
	}
	for(int x = 1; x <= n; x++){  //枚举1~n,试图将x填入P[index] 
		if(hashTable[x] == false){  //如果x不在P[0]~P[index-1]中 
			P[index] = x;  //令P的第index位为x,即把x加入当前排列 
			hashTable[x] = true;  //记x已在P中 
			generateP(index + 1);  //处理排列的第index+1号位 
			hashTable[x] = false;  //已处理完P[index]为x的子问题,还原状态 
		}
	}
} 

int main(){
	n = 8;  //欲输出8皇后问题全解 
	generateP(1);  //从P[1]开始填
	printf("%d\n", count);  //输出解的个数 
	return 0; 
}

2. 回溯法

  当已经放置了一部分皇后时,可能剩余的皇后不论怎么放都不可能合法,此时没有继续向下递归的必要,可直接返回上层以减少计算量。
  回溯法:如果在到达递归边界前的某层,由于一些事实导致已经不需要往任何一个子问题递归,就可以直接返回上一层。
修改generateP()函数如下:

void generateP(int index){
	if(index == n + 1){  //递归边界,生成一个合法方案 
		count++;  //能到达这里的一定是合法的 
		return;
	}
	for(int x = 1; x <= n; x++){  //第x行 
		if(hashTable[x] == false){  //第x行还没有皇后 
			bool flag = true;  //flag为true表示当前皇后不会和之前的皇后发生冲突 
			for(int pre = 1; pre < index; pre++){  //遍历之前的皇后 
			//第index列皇后的行号为x,第pre列皇后的行号为P[pre] 
				if(abs(index - pre) == abs(x - P[pre])){
					flag = false;  //与之前的皇后在一条对角线,冲突 
					break;
				}
			}
			if(flag){  //如果可以把皇后放在第x行 
				P[index] = x;  //令第index列皇后的行号为x 
				hashTable[x] = true;  //第x行已被占用 
				generateP(index + 1);  //递归处理第index+1行皇后 
				hashTable[x] = false;  //递归完毕,还原第x行为未占用 
			}
		}
	} 
} 

参考资料

[1]. 《算法笔记》P116-118

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

D-A-X

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值