n皇后问题(递归+回溯)

回溯与递归

递归:递归算法的实质是把问题分解成规模缩小的同类问题的子问题(分治),然后递归调用方法来表示问题的解。
回溯:按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。通过剪枝可以大幅减少解决问题的计算量。

全排列(1~n)

给出数字n,打印1~n的所有可能排列。

基本思路

设置数组P存储所求排列,hashTable数组作为标记数组,标记数字是否已加入排列。假设当前已经填好了P[1]-P[index-1],正准备填P[index]。于是枚举1-n,如果当前枚举到的数字x不在P[1]~P[index-1]中,那么把它填入P[index],然后处理P的第index+1位(递归)。当index = n + 1时,显然P[1]-P[n]已填好,进入递归边界,进行输出。

这里设n=3,代码如下:
#include <cstdio>
const int maxn = 11;

int n, P[maxn], hashTable[maxn] = {false};

void generateP(int index){ //处理第index号位 
	if(index == n + 1){ //已经处理完1~n位 
		for(int i = 1; i <= n; i++){
			printf("%d", P[i]);
		}
		printf("\n");
		return ;
	}
	for(int x = 1; x <= n; x++){ //试图将x填入P[index] 
		if(hashTable[x] == false){ //x不在P中 
			P[index] = x;  
			hashTable[x] = true;
			generateP(index + 1); //进入递归下一层
			hashTable[x] = false; //已处理完P[index]为x的子问题 
		}
	}
}

int main(){
	n = 3;
	generateP(1);
    return 0;
}

n皇后

n*n棋盘,放置n个皇后,要求任意两个皇后不在同一列、同一行、同一对角线上,求出方案数。

基本思路

可把n列皇后所在的行号依次写出,用全排列思想解决,每生成一个排列判断是否有处在同一条对角线上的皇后即可(同行同列显然不可能),没有则符合条件。但全排列需要n!次枚举,而在八皇后问题中,放置了一部分皇后时,剩余的皇后无论怎样放置都不合法,此时就没必要往下递归,直接返回上层即可。因此,以下采用回溯法,在摆第index个皇后时,判断其是否与先前的皇后冲突,若冲突,则返回上层。

代码
void generateP(int index){
	if(index == n + 1){
		count++;
		return;
	}
	for(int x = 1; x <= n; x++){ //枚举可能的行号
		if(hashTable[x] == false){ //该行没有皇后
			bool flag = true;//flag为true表示当前皇后不会与之前的冲突 
			for(int pre = 1; pre < index; pre++){//遍历之前的皇后 
				if(abs(index - pre) == abs(x - P[pre])){  //与之前的皇后在同一条对角线
					flag = false;
					break; //返回上一层
				}
			}
			if(flag){
				P[index] = x;
				hashTable[x] = true;
				generateP(index + 1);
				hashTable[x] = false;
			}
		}
	}
}

2n皇后

n* n棋盘,对应n* n个整数,若整数为1,表示可放皇后,整数不为1,表示不可放皇后。放置n个白皇后,n个黑皇后,使任意两个黑皇后不在同一行、同一列、同一对角线上,白皇后同理。求方案数。

基本思路

先放置白皇后,再放置黑皇后,其余思想与n皇后一致,只是在枚举每列行号的时候判断条件增多,具体见代码。

代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std; 

int black[10], white[10];
int m[10][10]; //地图
int n, cnt = 0;
bool hash_B[10] = {false}, hash_W[10] = {false};

bool judge(int P[], int index, int x){
	//判断当前摆放皇后是否与先前的冲突
	for(int pre = 1; pre < index; pre++){
		//index列皇后行号为x,pre列皇后行号为P[pre] 
		if(abs(index - pre) == abs(x - P[pre]))	
			return false;
	}
	return true; 
}

void generateB(int index){ //放置黑皇后
	if(index == n + 1){
		cnt++;
		return;
	} 
	for(int x = 1; x <= n; x++){
		//不冲突 && 可放置皇后 && 该位置没有白皇后 && 该行没有黑皇后 
		if(judge(black, index, x) && m[x][index] == 1 
		&& x != white[index] && hash_B[x] == false){
			black[index] = x;
			hash_B[x] = true;
			generateB(index + 1);
			hash_B[x] = false;
		} 
	}
}

void generateW(int index){ //放置白皇后
	if(index == n + 1){
		//放置完白皇后,开始放置黑皇后
		generateB(1);
		return; 
	} 
	for(int x = 1; x <= n; x++){
		//不冲突 && 可放置皇后 && 该行没有白皇后 
		if(judge(white, index, x) && m[x][index] == 1
		&& hash_W[x] == false){
			white[index] = x;
			hash_W[x] = true;
			generateW(index + 1);
			hash_W[x] = false;
		} 
	}
}

int main(){
	cin>>n;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			cin>>m[i][j];
	generateW(1);
	cout<<cnt<<endl;
	return 0;
}
发布了110 篇原创文章 · 获赞 0 · 访问量 1269

猜你喜欢

转载自blog.csdn.net/qq_43072010/article/details/105388833