DFS(深度優先搜索)求迷宮題的解法(附詳細註釋) - java語言

下面介紹DFS求迷宮題的解法,
DFS一般用於求迷宮中起點到終點的路徑總條數(BFS一般用於求最短距離)
下面以經典例題爲例,給大家DFS求迷宮題的解法的模板,習題1中DFS算法中附有詳細註釋,後面的習題中沒有

習題1:DFS_走字符迷宮

給一個n行m列的2維的迷宮,'S’表示迷宮的起點,‘T’表示迷宮的終點,
‘#‘表示不能通過的點,’.’ 表示可以通過的點。你需要從’S’出發走到’T’,
每次只能上下左右走動,並且只能進入能通過的點,每個點只能通過一次。
現在要求你求出有多少種通過迷宮的的方案。

輸入格式
第一行輸入n,m (1≤n,m≤10)表示迷宮大小。
接下來輸入n行字符串表示迷宮。

輸出格式
輸入通過迷宮的方法數。

/*
樣例1:
輸入
3 3
S..
.#.
..T

輸出
2

樣例2:
輸入
5 5
S.###
#....
#.#.#
#.#.#
#.#.T

輸出
1

樣例3:
輸入
6 6
...S..
.#.##.
......
...##.
.#.#..
.T....

輸出
57
*/
import java.util.*;
public class DFS_走迷宮 {
	
	static int MAXV=1005;//最大頂點數
	
	static int n,m;		 //大小爲n*m的地圖
	static int[][] map2=new int[MAXV][MAXV]; //二選一,輸入全爲數字的地圖
	static char[][] map=new char[MAXV][MAXV]; //二選一,輸入含有字符的地圖
	static int[][] vis=new int[MAXV][MAXV];	 //判定該點是否被訪問過,0爲未訪問,1爲訪問過
	
	static int count=0;    //最終結果,count記錄從起點到終點的路徑條數
  //move數組的元素順序,會決定遍歷時的順序,本例中順序爲“上下左右”
	static int move[][] = {{-1,0},{1,0},{0,-1},{0,1}};  
	
	//dfs算法
	public static void dfs(int x,int y) {	//x,y爲當前訪問的頂點座標。DFS算法的參數列表列出的都是變化的參數
	//有時必須要不寫check函數,而是通過dfs上來先檢查if滿足check條件,以便於後續的遞歸能繼續進行
		
		//1、進來之後,要幹什麼
		vis[x][y]=1;
		//如果需要進行某些操作,就在這裏進行
		
		//2、如果是終點,要幹什麼
		if(map[x][y]=='T') {	//出現目標態
			//做相應處理
			count++;
			return ;		//到達目標態,return返回上一層
		}
		
		//3、如果不是終點,要往後遞歸
		//下面對所有從(x,y)出發能達到的分支頂點進行枚舉
		for(int i=0;i<4;i++) {
			//不能直接改變傳入的參數x,y的值,要另設變量
			int nx=x+move[i][0];
			int ny=y+move[i][1];
//回溯法:在到達遞歸邊界前的某層,由於一些事實導致已經不需要往任何一個子問題遞歸,就可以直接返回上一層
//回溯法:先判斷,後遞歸   暴力法:先遞歸,後判斷
			if(check(nx,ny)) {  
				//滿足條件,進行某種操作,並將新點進行遞歸
				dfs(nx,ny);	
//因爲路徑有多條,因此要將路線和標記恢復成訪問前的狀態,爲遍歷下一條路徑時可能再次經過這個點做準備
				vis[nx][ny]=0;
			}
		}
	}
	
	//邊界條件和約束條件的判斷,條件由實際題目決定
	public static boolean check(int x,int y) {
		//注意是map[x][y]!='#',如果改成map[x][y]=='.',會忽略‘T’的情況
		if(x>=0&&y>=0&&x<n&&y<m&&map[x][y]!='#'&&vis[x][y]==0)
			return true;
		else return false;    //與約束條件衝突
	}
	
	
	public static void main(String[] args) {
		
		Scanner sc=new Scanner(System.in);
		
		//不能在n,m之前寫int
		n=sc.nextInt();
		m=sc.nextInt();

		int x=0,y=0;
		for(int i=0;i<n;i++) {
			String s=sc.next();
			for(int j=0;j<s.length();j++) {
				if(s.charAt(j)=='S') {
					x=i;
					y=j;
				}
			}
			map[i]=s.toCharArray();
		}
		
		dfs(x,y);
		
//		如果跑完dfs後還需要進行其他某些操作,就在這裏寫
		System.out.println(count);
		sc.close();

	}

}


得到了DFS的模板之後,再來練習一下下面這道數字迷宮的經典例題
本題已在題中給出數據,只需直接運行即可得到結果

習題2:DFS_走數字迷宮

給定一個M*N的矩陣(二維數組),分別用0和1表示通路和障礙物。
即 0 表示 通路;1 表示 障礙物。
從矩陣的左上角開始,每次只能向右,下,左,上移動位置,不能斜着走。
請給出從入口到出口的所有路線、最短路線、最短路線的長度。

import java.util.*;
public class DFS_走數字迷宮 {
	
	static int m,n,ans=0,l=0;
	static int[][] map = { 
			{0, 0, 1, 1, 1, 1, 1, 1, 1},  
            {1, 0, 0, 0, 0, 0, 0, 0, 1},  
            {1, 0, 1, 1, 0, 1, 1, 0, 1},  
            {1, 0, 1, 0, 0, 1, 0, 0, 1},  
            {1, 0, 1, 0, 1, 0, 1, 0, 1},  
            {1, 0, 0, 0, 0, 0, 1, 0, 1},  
            {1, 1, 0, 1, 1, 0, 1, 1, 1},  
            {1, 0, 0, 0, 0, 0, 0, 0, 0},  
            {1, 1, 1, 1, 1, 1, 1, 1, 0}
			};
	static int v[][];
	
	static String path="";
	static String shortestpath="";
	static int move[][]= {{1,0},{0,1},{0,-1},{-1,0}};
	
	public static void dfs(int x,int y) {
		
		v[x][y]=1;
		l++;

		if(x==m-1&&y==n-1) {
			path=path+"("+x+","+y+")";
			
			if(shortestpath.length()==0||path.length()<shortestpath.length()) {
				shortestpath=path;
			}
			System.out.println(path);
			if(ans==0||l<ans) {
				ans=l;
			}
			return ;
		}

		//temp用於遍歷完之後回退到本地址
		//temp的定義一定要在path增長後,否則會出錯
		path=path+"("+x+","+y+")->";
		String temp=path;
		
		for(int i=0;i<4;i++) {
			int newx=x+move[i][0];
			int newy=y+move[i][1];
			if(check(newx,newy)) {
				dfs(newx,newy);
				//將路線和標記恢復成上一次的狀態
				v[newx][newy]=0;
				path=temp;
				l--;
			}
		}
	}
	
	public static boolean check(int x,int y) {
		if(x>=0&&y>=0&&x<m&&y<n&&v[x][y]==0&&map[x][y]==0) {
			return true;
		}else return false;
	}
	
	public static void main(String[] args) {
		m=9;n=9;
		v=new int[m][n];
		dfs(0,0);
		System.out.println("最短路線爲:"+shortestpath);
		System.out.println("最短路線長度爲:"+ans);
		
//		(0,0)->(0,1)->(1,1)->(2,1)->(3,1)->(4,1)->(5,1)->(5,2)->(6,2)->(7,2)->(7,3)->(7,4)->(7,5)->(7,6)->(7,7)->(7,8)->(8,8)
//		(0,0)->(0,1)->(1,1)->(2,1)->(3,1)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)->(5,5)->(6,5)->(7,5)->(7,6)->(7,7)->(7,8)->(8,8)
//		(0,0)->(0,1)->(1,1)->(1,2)->(1,3)->(1,4)->(2,4)->(3,4)->(3,3)->(4,3)->(5,3)->(5,4)->(5,5)->(6,5)->(7,5)->(7,6)->(7,7)->(7,8)->(8,8)
//		(0,0)->(0,1)->(1,1)->(1,2)->(1,3)->(1,4)->(2,4)->(3,4)->(3,3)->(4,3)->(5,3)->(5,2)->(6,2)->(7,2)->(7,3)->(7,4)->(7,5)->(7,6)->(7,7)->(7,8)->(8,8)
//		最短路線爲:(0,0)->(0,1)->(1,1)->(2,1)->(3,1)->(4,1)->(5,1)->(5,2)->(6,2)->(7,2)->(7,3)->(7,4)->(7,5)->(7,6)->(7,7)->(7,8)->(8,8)
//		最短路線長度爲:17
	}
}

dfs算法除了應用於迷宮類型的題目之外,還可以應用於其他場景,例如下題用dfs算法求全排列

習題3:DFS_求全排列

假設給定正整數n,求從1到n的一共n個數 (1,2…n)能組合成的所有的三位數排列組合情況及情況個數。(數字可重複使用)
輸入描述:輸入正整數n
輸出描述:將能排列組合成的數從小到大輸出,每個數之間換行,最後輸出總個數

樣例1:
輸入:
1
輸出:
111
1

樣例2:
輸入:
3
輸出:
111
112
113
121
122
123
……
333
27

/** 

*/

import java.util.*;
public class DFS_求全排列 {
	
	static int[] a=new int[3];
	static int count=0;
	//參數index指第幾次從數組中取數
	public static void dfs(int index,int n) {
		
		if(index==3) {
			count++;
			for(int i=0;i<3;i++) {
				System.out.print(a[i]+" ");
			}
			System.out.println();
			
			return ;  
		}
		
		for(int i=1;i<=n;i++) {
				a[index]=i;
				dfs(index+1,n);
			}
			
		}

	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		dfs(0,n);
		System.out.println(count);
		sc.close();
	}
}

如果題目中再對輸出的數給出某些限制條件,比如能被3整除的數、素數,或者如果把排列組合成的“三位數”改成“四位數”等等,也只需要在此代碼上稍作修改就可以了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章