下面介紹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整除的數、素數,或者如果把排列組合成的“三位數”改成“四位數”等等,也只需要在此代碼上稍作修改就可以了。