DFS的题目一般不给出邻接矩阵,只给出迷宫图
DFS一般用于求图中起点到终点的路径条数
下面以经典例题为例,给大家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=999;//最大顶点数
static int n,m; //大小为n*m的地图
static int[][] map2=new int[MAXV][MAXV]; //二选一,输入全为数字的地图
static char[][] map=new char[MAXV][MAXV]; //二选一,输入含有字符的地图
static int[][] v=new int[MAXV][MAXV]; //判定该点是否被访问过,0为未访问,1为访问过
static int count=0; //最终结果,count记录从起点到终点的路径条数
//move数组的元素顺序,会决定遍历时的顺序
static int move[][] = { {1,0},{-1,0},{0,1},{0,-1}};
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算法
public static void dfs(int x,int y) { //x,y为当前访问的顶点坐标
//1、进来之后,要干什么
v[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);
//因为路径有多条,因此要将路线和标记恢复成访问前的状态,为遍历下一条路径做准备
v[nx][ny]=0;
}
}
//return;
}
//边界条件和约束条件的判断,约束条件由实际题目决定
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]!='#'&&v[x][y]==0)
return true;
else //与约束条件冲突
return false;
}
}
得到了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);
}
}
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();
//如果省略了下面的if条件,则return 不可缺少
return ;
}
// if(index>=0&&index<=n) {
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整除的数、素数,或者如果把排列组合成的“三位数”改成“四位数”,相信大家也都会做了。