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