问题描述:小哈在m行n列的矩形迷宫里迷了路,小哼来解救小哈。已知小哈在迷宫的坐标为(p, q),问小哼最少要走多少步才能走到小哈的所在位置。
使用DFS解决该问题需要了解:
Question1. 递归时结束的条件
Question2.递去的过程应该做什么
Question3.归来的过程应该做什么
Answer1.很显然,可以用一个二维数组来初始化迷宫,当小哼的坐标和小哈的坐标一致时,递归结束
void dfs(int x, int y, int step) {
if (x == p&& y == q)
{
total++;
if (step < min) //如果step比min小,就修改min的值
min = step;
return; //如果到达了终点就回溯
}
Answer2.递去的过程中,需要首先规定一个顺序 本文章代码规定顺时针顺序移动,即 右下左上
可以用多个if 语句并列实现,也可以使用方向数组实现,本文章代码将用c语言展示if语句实现 ,方向数组的用java实现。规定完顺序后 考虑按照顺序改变后坐标是否满足 1.未被标记 和 2.是空地 3.未越界的问题。
Answer3. 归来的过程中,需要把当前坐标 重新标记为未标记
else {
if ( y + 1 <= n && a[x][y+1] == 0 && book[x][y+1]==0) //判断边界以及该坐标是否被标记和是否是空地
{
book[x][y+1] = 1; // 递去阶段:当走到当前位置,标记已走过
dfs(x , y+1, step + 1); // 进行该位置的深度优先搜索
book[x][y+1] = 0; // 回溯阶段: 取消该标记,并按顺序继续移动
}
if ( x + 1 <= m && a[x+1][y] == 0 && book[x+1][y ] == 0)
{
book[x+1][y] = 1;
dfs(x+1, y , step + 1);
book[x+1][y] = 0;
}
if (0 < y -1 && a[x][y -1] == 0 && book[x][y - 1] == 0)
{
book[x][y -1] = 1;
dfs(x, y - 1, step + 1);
book[x][y - 1] = 0;
}
if (0 < x -1 && a[x - 1][y] == 0 && book[x -1][y] == 0)
{
book[x - 1][y] = 1;
dfs(x -1, y, step + 1);
book[x -1][y] = 0;
}
return; //当无路可走时 回溯上一个位置;
}
完整C语言代码如下:
#include <stdio.h>
int p, q,m, n,min=888; //设置行数m 与列数n (p,q)为终点的坐标
int total = 0; //路径总数
int book[51][51]; //标记数组 用于判断下个坐标是否走过 走过则标记为1
int a[51][51]; // 0表示空地 1表示障碍物
void dfs(int x, int y, int step) {
if (x == p&& y == q)
{
total++;
if (step < min) //如果step比min小,就修改min的值
min = step;
return; //如果到达了终点就回溯
}
else {
if ( y + 1 <= n && a[x][y+1] == 0 && book[x][y+1]==0) //判断边界以及该坐标是否被标记和是否是空地
{
book[x][y+1] = 1; // 递去阶段:当走到当前位置,标记已走过
dfs(x , y+1, step + 1); // 进行该位置的深度优先搜索
book[x][y+1] = 0; // 回溯阶段: 取消该标记,并按顺序继续移动
}
if ( x + 1 <= m && a[x+1][y] == 0 && book[x+1][y ] == 0)
{
book[x+1][y] = 1;
dfs(x+1, y , step + 1);
book[x+1][y] = 0;
}
if (0 < y -1 && a[x][y -1] == 0 && book[x][y - 1] == 0)
{
book[x][y -1] = 1;
dfs(x, y - 1, step + 1);
book[x][y - 1] = 0;
}
if (0 < x -1 && a[x - 1][y] == 0 && book[x -1][y] == 0)
{
book[x - 1][y] = 1;
dfs(x -1, y, step + 1);
book[x -1][y] = 0;
}
return; //当无路可走时 回溯上一个位置;
}
}
int main()
{
int st, ed;
printf("请分别输入迷宫的行数和列数 m 和 n (两者均<=50)\n");
scanf_s("%d %d", &m, &n);
printf("请读入迷宫,可走的路径设置为0 障碍物设置为1\n");
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
scanf_s("%d", &a[i][j]);
printf("请输入起点坐标\n");
scanf_s("%d %d", &st, &ed);
book[st][ed] = 1;
printf("请输入终点坐标\n");
scanf_s("%d %d", &p, &q);
printf("NO problem\n");
dfs(st, ed, 0);
printf("找到了%d条路径\n最短路径为%d\n",total, min);
}
结果展示:
为计算方便,本算法所有数组从1开始
下面展示java代码:
与c的代码大同小异,使用了方向数组,做这类DFS的题目时经常用到,建议掌握
import java.util.Scanner;
public class DFS2 {
private int m,n,p,q;
private boolean book[][]=new boolean[51][51];
private int a[][]=new int[51][51];
private int min=8888,total=0;
private int Dire[][]={{0,1},{1,0},{0,-1},{-1,0}}; //方向数组,用于改变小哼当前的坐标(相比于分支结构if减少了代码的冗余性并增加了美观)
public void dfs(int x,int y,int step){
int tx,ty;
if(x==p&&y==q) {
total++;
if(min>step) min=step;
return;
}
else {
for(int i=0;i<4;i++){
tx=x+Dire[i][0]; //改变坐标
ty=y+Dire[i][1];
if(tx<=m && tx>0 && ty>0 && ty<=n &&a[tx][ty]==0&&book[tx][ty]==false){ //判断当前即将移动的坐标是否满足条件
book[tx][ty]=true; //递去过程中 标记当前位置已走过
dfs(tx,ty,step+1);
book[tx][ty]=false; //归来过程中 取消标记
}
}
return;
}
}
public static void main(String[] args) {
DFS2 d=new DFS2();
Scanner in=new Scanner(System.in);
System.out.println("请输入迷宫的行数和列数");
d.m=in.nextInt(); d.n=in.nextInt();
System.out.println("请输入迷宫 0代表空地 1代表障碍物");
for(int i=1;i<=d.m;i++)
for(int j=1;j<=d.n;j++)
d.a[i][j]=in.nextInt();
System.out.println("请输入起点的坐标");
int st=in.nextInt(),ed= in.nextInt();
System.out.println("请输入终点的坐标");
d.p= in.nextInt();d.q= in.nextInt();
d.book[st][ed]=true;
d.dfs(st,ed,0);
System.out.printf("总共找到%d条路径\n其中最短路径长度为%d",d.total,d.min);
}
}
结果展示:
此类题目刚开始做时很头疼,关键是找好递去归来过程中需要完成什么事情才能有效解决问题
建议现在草稿纸上好好研究模型在动手打代码也许更加容易理解
如果这篇文章有帮到大家的话,希望大家点个赞哦,谢谢啦!