1. 程式人生 > 其它 >n皇后暴力&回溯法

n皇后暴力&回溯法

技術標籤:遞迴

【問題描述】
在一個n*n棋盤上放n個皇后,滿足n個皇后兩兩均不在同一行、同一列、同一條對角線上,求滿足條件的方案數。

【思路】

1.暴力法

如果直接想暴力列舉所有情況,從nn個位置中選擇n個位置,則需要Cnn n種情況,如n=8時就需要54 502 232種情況,挺大的數字了;
但如果想:
每行、列均只能放置一個皇后,而現在把n列皇后所在的行號依次寫出,相當於一個1~n的全排列,即只有n!種情況。
可以回顧之前的文章:遞迴實現全排列&使用STL
現在就在全排列的基礎上求解。由於當到達遞迴邊界時表示生成了一個排列,所以需要在其遞迴邊界內部判斷是否為合法方案,即遍歷每2個皇后,判斷它們是否在同一條對角線上,顯然不在同一行和同一列。

#include<stdio.h>
#include<string>
#include<vector>
#include<cmath>
const int maxn=11;
int count=0;
//P為當前排列,hashtable記錄整數x是否已經在P中
int n,P[maxn],hashtable[maxn]={false};
//當前處理排列的第index號位
void generateP(int index){
	if(index==n+1){//遞迴邊界,已經處理完排列的1~n位
		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])){//如果在一條對角線上 flag=false;//不合法 } } } if(flag) count++;//若當前方案合法,令count加1 //printf("count=%d",count); 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;//欲輸出1~8的全排列,且滿足8皇后條件 generateP(1);//從P[1]開始填 printf("count=%d",count); //return 0; system("pause"); }

上面這種列舉所有情況再判斷是否合法,屬於【暴力法】即沒有用優化演算法、直接用樸素演算法。

2.回溯法

【回溯法】如果在到達遞迴邊界前的某層,由於一些事實導致已經不需要任何一個子問題遞迴,就可以直接返回上一層。
栗子:
在這裡插入圖片描述
上面前三個皇后(即351)放置好後,剩下2個皇后不管怎麼放置都會發生衝突,即沒必要繼續遞迴了。

#include<stdio.h>
#include<stdlib.h>
const int maxn=11;
int count=0;
//P為當前排列,hashtable記錄整數x是否已經在P中
int n,P[maxn],hashtable[maxn]={false};
//當前處理排列的第index號位
void generateP(int index){
	if(index==n+1){//遞迴邊界,已經處理完排列的1~n位
		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行為未佔用
			}
		}
	}
}
int main(){
	n=8;//欲輸出1~8的全排列,且滿足8皇后條件
	generateP(1);//從P[1]開始填
	printf("count=%d",count);
	//return 0;
	system("pause");
}