寫在前面:
回溯法非常適合由多個步驟組成的問題,並且每個步驟都有多個選項。當我們在某一步選擇了其中一個選項時,就進入下一步,然後又面臨新的選項。我們就這樣重複選擇,直至達到最終的狀態。
通常回溯法算法適合用遞歸實現代碼。當我們到達某一個節點時,嘗試所有可能的選項並在滿足條件的前提下遞歸地遞達下一個節點。
我們介紹兩個簡單的例題,通過並不複雜的代碼來好好理解一下。
例1:請設計一個函數用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從任意一格開始,每一步可以向上,下,左,右移動。如果一條路徑經過了矩陣的某一格,那麼該路徑不能再次進入該格子。
//利用 回溯法 去解決
bool exist_path(char* matrix,int rows,int cols,char* str){
if(matrix==nullptr || rows<1 || cols<1 ||str==nullptr )
return false;
bool *visted=new bool[rows*cols];
memset(visted,0,rows*cols);
int pathlength=0;
for(int i=0;i<rows;++i){
for(int j=0;j<cols;++j){
if(has_path(maxrix,rows,cols,row,col,str,pathlength,visted)){
return true;
}
}
}
deleted []visted;
return false;
}
bool has_path(char* maxrix,int rows,int cols,int row,int col,const char* str,int& pathlength,bool *visted){
if(str[length]='\0')
return true;
bool haspath=false;
if(row>=0&&row<rows && col>=0&&col<cols && !visted[row*cols+col] && maxrix[row*cols+col]==str[pathlength]){
//如果命中了
++pathlength;
visted[row*cols+col]=true;
haspath=has_path(maxrix,rows,cols,row-1,col,str,pathlength,visted)
|| has_path(maxrix,rows,cols,row+1,col,str,pathlength,visted)
||has_path(maxrix,rows,cols,row,col+1,str,pathlength,visted)
||has_path(maxrix,rows,cols,row,col-1,str,pathlength,visted);
if(!haspath){
--pahthlength;
visted[row*col]=false;
}
}
}
例2:經典的八皇后問題
//八皇后問題應該算是回溯法中很典型的一個例子。很有意思,我們來說一下。
//國際象棋中的皇后,可以橫向,縱向,斜向移動。如何在一個8*8的棋盤上,放置八個皇后,
//使得任意兩個皇后都不在同一條橫線,豎線,斜線方向上?
1 #include<iostream>
2 #include<math.h>
3 using namespace std;
4
5 int n=8;
6 int total=0;
7 int *c=new int(n);
8
9 bool is_ok(int row){
10 for(int j=0;j!=row;j++){
//if語句後兩個條件是爲了表示兩個皇后呈斜線或者反斜線
11 if(c[row]==c[j] || row-c[row]==j-c[j] || row+c[row]==j+c[j])
12 return false;
13 }
14 return true;
15 }
16
17 void queen(int row){
18 if(row==n)
19 total++;
20 else
21 for(int col=0;col!=n;col++){
22 c[row]=col;
23 if(is_ok(row))
24 queen(row+1);
25 }
26 }
27
28 int main(){
29 queen(0);
30 cout<<total;
31 return 1;
32 }
33
這個八皇后問題解法應該是相當簡化的一個版本啦,邏輯也很清晰,大家可以琢磨一下代碼再理解回溯的思想。看不懂的地方可以評論區留言。