遞迴——以全排列和n皇后問題舉例
阿新 • • 發佈:2018-11-21
筆記來自【晴神寶典】
一、遞迴
遞迴 就在於反覆呼叫自身函式,但是每次都把問題範圍縮小,直到範圍可以縮小到可以直接得到邊界資料的結果,然後在返回路上求出對應的解。以上可看出,遞迴很適合用來實現分治思想。
遞迴兩個很重要的組成組成:
1、遞迴邊界(出口);
2、遞迴式。
二、全排列 (Full Permutation)
【可畫圖,便於理解】
#include <cstdio> const int maxn=100; //P為當前排列,hashTable記錄整數x是否已經在P中 int n, P[maxn], hashTable[maxn]={false}; //當前處理排列的第index號位 void generateP(int index){ if(index==n+1){ for(int i=1; i<=n; i++){ printf("%d", p[i]); } printf("\n"); return ; } for(int x=1; x<=n; x++){ if(hashTable[x]==false){ 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個皇后兩兩均不在同一行、同一列、同一條對角線上,求合法的方案數。
1、暴力法(列舉所有情況)
#include <cstdio> #include<math.h> const int maxn=100; int n, P[maxn], hashTable[maxn]={false}; int count=0; void generateP(int index){ if(index==n+1){ bool 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++; return ; } for(int x=1; x<=n; x++){ if(hashTable[x]==false){ P[index]=x; hashTable[x]=true; generateP(index+1); hashTable[x]=false; //已處理完p[index]為x的子序列問題,還原狀態 } } } int main() { n=8; generateP(1); printf("%d", count); //92 return 0; }
2、回溯法
到達遞迴邊界前的某層時,由於一些事實導致已經不需要再往下遞迴了,就直接返回上一層。
#include <cstdio> #include<math.h> const int maxn=100; int n, P[maxn], hashTable[maxn]={false}; int count=0; void generateP(int index){ if(index==n+1){ count++; //能到遞迴邊界一定是合法的 return ; } for(int x=1; x<=n; x++){ if(hashTable[x]==false){//第x行還沒有皇后 bool flag=true; for(int pre=1; pre<index; pre++){//遍歷之前的皇后 if(abs(index-pre)==abs(x-P[pre])){//是否與之前的皇后對角線衝突 flag=false; break; } } if(flag==true){ P[index]=x; hashTable[x]=true; generateP(index+1); hashTable[x]=false; //已處理完p[index]為x的子序列問題,還原狀態 } } } } int main() { n=8; generateP(1); printf("%d", count); //92 return 0; }