n皇后暴力&回溯法
阿新 • • 發佈:2021-01-08
技術標籤:遞迴
【問題描述】
在一個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");
}