深度優先搜尋演算法

來源:互聯網
上載者:User

下面是poj3009 curling2.0的題目。

http://acm.pku.edu.cn/JudgeOnline/problem?id=3009

 

使用的是dfs搜尋,代碼如下,從中可以體會到dfs搜尋的常規特點:

 

//POJ3009 Curling 2.0<br />//使用深度搜尋,這題有兩個地方需要注意:<br />//1、當球與箱子相鄰的時候,球無法滾動,也無法裝破箱子。<br />//2、題目有一個限界,當球滾了10次還沒有進入目標點,看做不能到達。</p><p>#include<iostream><br />#include<cstdio><br />#include<fstream><br />#define __DEBUG 0<br />using namespace std;</p><p>#define MAX 30<br />int map[MAX][MAX];<br />int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};<br />int row,column,step,minStep;</p><p>int find(int x,int y){//檢查球是否在board範圍內</p><p>if( x>=0 && x<row && y>=0 && y< column)<br />return 1;<br />else<br />return 0;<br />}</p><p>void DFS(int step,int x,int y){</p><p>if( step >= 10 )//剪枝條件“>=10”放棄搜尋<br />return;</p><p>step++;//換一個方向才算步數加1</p><p>for(int k=0;k<4;k++){</p><p>int nextX=x+dir[k][0];<br />int nextY=y+dir[k][1];</p><p>if( map[nextX][nextY]!=1 && find(nextX,nextY)){ //球的下一步不能是block,同時不能超出board的範圍</p><p>while( map[nextX][nextY]!=3 && map[nextX][nextY]!=1 && find(nextX,nextY) ){//找到目標點停止 遇到block停止 超出board的範圍停止</p><p>nextX += dir[k][0];//朝先前的方向一直走下去,知道遇到block或target<br />nextY += dir[k][1];</p><p>}</p><p>if( map[nextX][nextY]==3){//到達目標點<br />if(step<minStep)<br />minStep=step;<br />//cout<<minStep<<endl;<br />return;<br />}</p><p>else if( map[nextX][nextY]==1){//撞到block</p><p>map[nextX][nextY]=0;//清空block<br />nextX -= dir[k][0];<br />nextY -= dir[k][1];<br />DFS(step,nextX,nextY);//繼續搜尋<br />map[nextX][nextY]=1;//如果某次DFS失敗未能找到目標點,就需要恢複block,重新開始一次搜尋<br />}<br />}<br />}</p><p>return ;</p><p>}</p><p>int main(){</p><p>//ifstream in("test.txt");<br />int startX,startY;</p><p>while(1){</p><p>//in>>column>>row;<br />scanf("%d %d",&column ,&row);<br />if( column ==0 && row==0)<br />break;</p><p>for(int i=0; i<row ;i++){<br />for(int j=0; j<column ;j++){</p><p>//in>>map[i][j];<br />scanf("%d",&map[i][j]);<br />if( 2 == map[i][j]){</p><p>startX=i;<br />startY=j;</p><p>}<br />}<br />}</p><p>if(__DEBUG){</p><p>cout<<startX<<" "<<startY<<endl;<br />}</p><p>minStep=11;<br />DFS(0,startX,startY);</p><p>if(minStep<=10)//如果步數超過10次,搜尋失敗,不存在合適的最短路徑<br />cout<<minStep<<endl;<br />else<br />cout<<-1<<endl;<br />}</p><p>return 0;</p><p>}<br />

 

總結一下:

 

1、使用dfs搜尋求解最優解,往往需要需要多次深入搜尋和回溯的過程,然後選擇所有搜尋到的解中最優的解。還有就是dfs搜尋返回時,一般都是需要恢複原來狀態的,以便於下次的搜尋。

 

下面是POJ2676 Sudoku:

http://acm.pku.edu.cn/JudgeOnline/problem?id=2676

 

這道題目的本質在於使用dfs回溯的思想解題。這種方法尤其是在存在多個解,只要找到一個解的情況下,非常有用。

 

//POJ2676:Sudoku<br />/*<br />用1-9的數字去填充上面的9*9表格,要求如下<br /> 1.每一行的九個格子中1-9各出現一次<br /> 2.每一列的九個格子中1-9各出現一次<br /> 3.還有如用粗線隔開的3*3的小塊的九個格子中1-9個數字也各出現一次<br /> 有可能不止一種解,只要輸出一種合適的解即可</p><p>*/<br />/*<br />思路:使用回溯法,DFS<br />並使用標記法剪枝<br />靜下心來分析,不難<br />*/</p><p>#include<iostream><br />#include<fstream><br />#include<ctime><br />using namespace std;</p><p>bool rUsed[9][10],cUsed[9][10],sUsed[9][10];//用於標記某行、某列、某個3*3小方格上哪些數字已經被使用過了<br />int pos[100];//還沒有填充數位方格位置<br />int nullNum;<br />int table[9][9];<br />bool DFS_SUDO;</p><p>void print(){</p><p>for(int i=0;i<9;i++){<br />for(int j=0;j<9 ;j++)<br />cout<<table[i][j]<<" ";<br />cout<<endl;<br />}</p><p>}</p><p>void DFS(int n){</p><p>if(n>=nullNum){<br />DFS_SUDO=true;<br />print();<br />return;<br />}</p><p>int r=pos[n]/9;//在第r行<br />int c=pos[n]%9;//在第c列<br />int k= (r/3)*3+(c/3);//在第k個小方格<br />for(int i=1; i<=9 && !DFS_SUDO;i++){</p><p>if( cUsed[c][i] ) continue;<br />if( rUsed[r][i] ) continue;<br />if( sUsed[k][i] ) continue;<br />cUsed[c][i]=rUsed[r][i]=sUsed[k][i]=true;<br />table[r][c]=i;<br />DFS(n+1);<br />table[r][c]=0;//如果DFS搜尋失敗就回溯<br />cUsed[c][i]=rUsed[r][i]=sUsed[k][i]=false;<br />}<br />return;<br />}</p><p>int main(){</p><p>ifstream in("test.txt");<br />int testCase;<br />in>>testCase;<br />while(testCase--){</p><p>nullNum=0;<br />char line[10];<br />for(int i=0;i<9;i++)//初始化<br />for( int j=0;j<10 ;j++)<br />rUsed[i][j]=cUsed[i][j]=sUsed[i][j]=false;</p><p>for(int i=0 ;i<9 ;i++){<br />in>>line;//讀入一行<br />//cout<<line;<br />for(int j=0;j<9;j++){</p><p>table[i][j]=line[j]-'0';</p><p>if(table[i][j]){</p><p>rUsed[i][ table[i][j] ]=true;//第i行用過這個數<br />cUsed[j][ table[i][j] ]=true;//第j列用過這個數<br />int k= (i/3)*3 + (j/3);//第k個3*3方格<br />sUsed[k][ table[i][j] ]=true;</p><p>}<br />else<br />pos[nullNum++]=9*i+j;//使用數組,記錄沒有填數的小方格的位置<br />}<br />}</p><p>DFS_SUDO=false;<br />//clock_t time = clock();<br />DFS(0);<br />//cout<<"time used: "<<clock()-time<<" MS"<<endl;</p><p>}<br />return 0;<br />}<br />

 

 

這裡設計的巧妙之處是:使用二維數組直接把已經存在的數字記錄下來,而把沒有填數位位置用數組記錄下來,這樣在dfs搜尋的時候就非常方便。可以看出在題目的演算法設計時,選擇合適和便於處理的資料結構有事半功倍的效果。

 

下面是POJ2488 A knight's journey的題目:

http://acm.pku.edu.cn/JudgeOnline/problem?id=2488

 

//POJ2488 A knight's journey<br />//這裡使用DFS搜尋策略來完成遍曆棋盤上的所有格子<br />//騎士的前進方法可以使用方向二維數組來表示</p><p>#include<iostream><br />using namespace std;</p><p>#define MAXSIZE 30</p><p>int board[MAXSIZE][MAXSIZE];//board<br />int stepX[MAXSIZE],stepY[MAXSIZE];//記錄當前在棋盤上的位置<br />int dir[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};//the direction of the knight<br />int p,q;//記錄棋盤的x,y<br />int sign;//用來標記棋盤的所有空格都已經走完<br />int step;//記錄當前的步數</p><p>void DFS(int i,int j){//DFS search</p><p>if( sign) return;//base case,如果遍曆完則直接返回</p><p>step++;<br />stepX[step]=i;//記錄當前座標<br />stepY[step]=j;</p><p>board[i][j]=1;//標記</p><p>if( step == p*q){//全部走完了<br />sign=1;<br />return ;<br />}</p><p>for(int k=0; k<8 ;k++){</p><p>int x=i+dir[k][0];<br />int y=j+dir[k][1];<br />if( x>=1 && x<=p && y>=1 && y<=q && board[x][y]==0 ){</p><p>DFS(x,y);<br />step--;//如果DFS搜尋失敗,則step--<br />}</p><p>}</p><p>board[i][j]=0;//失敗,則對座標(i,j)清零,回溯<br />}</p><p>int main(){</p><p>int testNum;<br />int t=0;//記錄第幾次測試<br />cin>>testNum;</p><p>while( testNum--){</p><p>sign=0;//每次開始之前對sign進行置零<br />step=0;<br />t++;</p><p>cin>>p>>q;<br />for(int i=1;i<=p;i++)<br />for(int j=1;j<=q;j++)<br />board[i][j]=0;//initialize the board</p><p>DFS(1,1);<br />cout<<"Scenario #"<<t<<":"<<endl;<br />if( sign ){</p><p>for(int i=1;i<=p*q ; i++)//這裡不能使用i<=step來判斷,因為step在遞迴返回時,遞減為1了<br />cout<<char(stepY[i]+'A'-1)<<stepX[i];//注意這裡要轉化為char類型<br />cout<<endl;<br />}<br />else<br />cout<<"impossible"<<endl;</p><p>if(testNum!=0)//最後一個senario沒有空行<br />cout<<endl;</p><p>}<br />return 0;<br />}<br />

 

這也是使用dfs回溯的思想來解題的,總結一下:

1、由於dfs回溯法,一般都是寫成遞迴的形式,所以寫的時候要細心一點,尤其是在回溯的時候,要進行還原。在這裡dfs函數中不能漏泄最後的回溯還原 :board[i][j] = 0,這個條件表明當走到當前這一步後,後面都是無效的走步時,需要撤銷當前的這一步位置的標記。

2、在dfs搜尋過程中還是能夠記錄下搜尋路徑,便於後面的還原。比如:這道題目中就是使用一個數組來記錄路徑的。

 

 

下面是POJ3083 Children of the Candy Corn的題目,使用的同樣是深度優先搜尋:

 

 //POJ3083 Children of the Candy Corn<br />//這裡求解最短路徑的步數使用BFS進行搜尋,而求解left path和right path使用的步數使用DFS搜尋<br />//使用矩陣來表示maze,同時利用元素的位置[i,j],作為座標<br />//有時終端未顯示任何結果,但正常終止,可能是發生了異常(如棧的溢出)</p><p>#include<iostream><br />#include<queue><br />#include<fstream><br />using namespace std;</p><p>#define MAX 45<br />//定義全域變數<br />int width,height;<br />char map[MAX][MAX];<br />int dir[2][4]={{1,0,-1,0},{0,1,0,-1}};//標記移動的方向,依次為:下、右、上、左(x座標,y座標)<br />int startX,startY;//記錄maze的起點座標<br />int lFlag,lStep,rStep;//lFlag變數標記執行left path或right path</p><p>struct Node{<br />int x;<br />int y;<br />int step;//記錄最短路徑的步數<br />Node(int x1,int y1,int s):x(x1),y(y1),step(s){}<br />};</p><p>int BFS(int x,int y){//BFS一般使用隊列的資料結構,並使用非遞迴形式</p><p>queue<Node> q;<br />Node n=Node(x,y,1);</p><p>q.push(n);<br />while( !q.empty()){</p><p>Node tmp=q.front();<br />q.pop();</p><p>for( int k=0;k<4;k++){//在四個方向上進行判斷</p><p>int nextX=tmp.x+dir[0][k];//這裡是在前一個結點的基礎上試探,所以使用的是tm.x/tmp.y,而不是x/y<br />int nextY=tmp.y+dir[1][k];</p><p>if( nextX>=0 && nextX<height && nextY>=0 && nextY<width && map[nextX][nextY]!='#'){</p><p>if( map[nextX][nextY]=='E')//如果找到出口,就返回<br />return tmp.step+1;<br />q.push( Node(nextX,nextY,tmp.step+1));<br />map[tmp.x][tmp.y]='#';//設定已到達結點以後不可達,否則會陷入死迴圈<br />}<br />}</p><p>}<br />return 0;</p><p>}</p><p>int DFS(int x,int y,int z){//DFS一般使用遞迴實現<br />//z表示方向<br />if( map[x][y] == 'E')<br />return 1;</p><p>for( int k=0; k<4 ;k++){</p><p>int nextX=x+dir[0][z];<br />int nextY=y+dir[1][z];</p><p>//if( nextX>=0 && nextX<width && nextY>=0 && nextY<height && map[nextX][nextY]!='#'){//下一步可以到達<br />if( map[nextX][nextY]=='E' || map[nextX][nextY]=='.'){</p><p>if(lFlag){//向左搜尋<br />lStep++;<br />if( z==3)<br />z=-1;<br />if(DFS(nextX,nextY,z+1))//方向加1,方向向左<br />return 1;<br />}</p><p>else{</p><p>rStep++;<br />if( z==0)<br />z=4;<br />if(DFS(nextX,nextY,z-1))//方向減1,方向向右<br />return 1;</p><p>}</p><p>}</p><p>if( lFlag){//向左搜尋,順時針旋轉方向 </p><p>z--;<br />if(-1==z)//if中的“==”判斷容易出錯,把常量寫在左邊,容易查錯<br />z=3;<br />}<br />else{//向右搜尋,逆時針旋轉方向 </p><p>z++;<br />if(z==4)<br />z=0;<br />}<br />}<br />return 0;//如果找不到下一步可達的方格,就返回0<br />}</p><p>int main(){</p><p>ifstream in("test.txt");</p><p>int count;<br />in>>count;</p><p>while(count--){//count--,先判斷,後執行減運算</p><p>in>>width>>height;<br />for(int i=0;i<height ; i++){<br />//這樣直接輸入的前提是字元之間不能有空格,因為空白格也會被當做一個字元<br />in>>map[i];<br />//cout<<map[i]<<endl;<br />for( int j=0; j< width ;j++)<br />if( map[i][j] == 'S'){<br />startX=i;<br />startY=j;<br />}<br />}</p><p>lFlag=1;<br />lStep=1;<br />DFS(startX,startY,0);<br />lFlag=0;<br />rStep=1;<br />DFS(startX,startY,0);</p><p>cout<<lStep<<" "<<rStep<<" "<<BFS(startX,startY)<<endl;</p><p>}<br />}<br />

 

這道題目中,由於要求就出的是向左走和向右走為高優先順序的路徑,所以在方向的變換上要注意:向左走,就需要始終保持以左方向為高優先順序,這點是最重要的。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.