回溯法非常适合由多个步骤组成的问题,并且每个步骤都有多个选项。当我们在某一步选择了其中一个选项时,就进入下一步,然后又面临新的选项。我们就这样重复选择,直至达到最终的状态。 通常回溯法算法适合用递归实现代码。当我们到达某一个节点时,尝试所有可能的选项并在满足条件的前提下递归地递达下一个节点。接下来我们介绍两个简单的例题,通过并不复杂的代码来好好理解一下。
例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这个八皇后问题解法应该是相当简化的一个版本啦,逻辑也很清晰,大家可以琢磨一下代码再理解回溯的思想。看不懂的地方可以评论区留言。