一、概念
深度优先搜索(简称
DFS
),其工作原理为不撞南墙不回头,能深则深,不能则退。最差情况下的时间复杂度为 O ( a n ) O(a^n) O(an), a a a 为可选条数。
二、典型题目
1. 题目
给定一个
n
n
n 行
m
m
m 列的迷宫,有些格子可以走,有些有障碍物不能到达。每步可以走到上下左右的格子中。请你判断,是否能从左上角走到右下角。如果能走到输出 YES
,否则输出 NO
。迷宫中字符为 *
表示迷宫这个格子有障碍物,.
表示没有障碍物。
2. 分析
不能走的地方
- 迷宫的边界
- 遇到障碍物
- 走回头路
DFS
的功能
在一个点遍历 4 4 4 个方向,如果这个方向上的点满足条件,去下一个点。
伪代码
dfs(x, y)
if (x == n && y == m)
stop
if (isRoad(a[?][?]))
dfs(?, ?);
...
3. 参考答案
#include <iostream>
using namespace std;
int n, m; // 迷宫大小
bool flag; // 是否有解
char Map[25][25]; // 地形图
bool vis[25][25]; // 标记是否走过
int dx[5] = {-1, 0, 1, 0}; // 四个方向的偏移量
int dy[5] = {0, 1, 0, -1}; // 四个方向的偏移量
void dfs(int x, int y)
{
// 到终点
if (x == n && y == m)
{
flag = true;
return;
}
// 遍历方向,判断是否满足条件
for (int i = 0; i < 4; i++)
{
int tmpX = x + dx[i];
int tmpY = y + dy[i];
// 是通路
if (Map[tmpX][tmpY] == '.')
{
// 未到边界
if (tmpX >= 1 && tmpX <= n && tmpY >= 1 && tmpY <= m)
{
// 未访问
if (vis[tmpX][tmpY] == false)
{
vis[tmpX][tmpY] = true;
dfs(tmpX, tmpY);
}
}
}
}
}
int main()
{
// 输入
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> Map[i][j];
}
}
// dfs
vis[1][1] = 1;
dfs(1, 1);
// 输出
cout << (flag ? "YES" : "NO");
return 0;
}
三、变形题目
1. 路径数量
1.1 审题
给定一个 n n n 行 m m m 列的矩阵,可以向上下左右四个方向移动,找到能从 ( s x , s y ) (sx, sy) (sx,sy) 走到 ( e x , e y ) (ex, ey) (ex,ey) 的所有路径数。
1.2 思路
这个题目和典型题目非常类似,只是多了回溯 ( 1 ) ^{(1)} (1)的步骤。
回溯:
通过逐步试探和撤销的方式来查找问题的解,这种算法就叫回溯法。
优点:
- 简单直观
- 适用于穷举搜索
- 可以找到所有的解
缺点:
- 效率低
- 难以优化
- 可能重复计算
2. 走迷宫的过程
2.1 审题
给定一个 n n n 行 m m m 列的迷宫,有些格子可以走,有些有障碍物不能到达。每步可以走到上下左右的 4 4 4 个格子中。请你输出从左上角开始,走到右下角的路径。其中,从一个格子出发,优先出发的顺序为:上、右、下、左。输出的路径坐标之间使用空格隔开,由于路径可能有多条,每条路径之间使用换行隔开。
2.2 参考答案
#include <iostream>
using namespace std;
int n, m; // 迷宫大小
char Map[25][25]; // 地形图
bool vis[25][25]; // 标记是否走过
int dx[5] = {-1, 0, 1, 0}; // 四个方向的偏移量
int dy[5] = {0, 1, 0, -1}; // 四个方向的偏移量
int pos = 1; // 路线的长度
int ansx[85] = {1}; // 路线的 x 坐标
int ansy[85] = {1}; // 路线的 y 坐标
void dfs(int x, int y)
{
// 到终点
if (x == n && y == m)
{
for (int i = 0; i <= pos-1; i++)
{
cout << "(" << ansx[i] << "," << ansy[i] << ") ";
}
cout << endl;
return;
}
// 遍历方向,判断是否满足条件
for (int i = 0; i < 4; i++)
{
int tmpX = x + dx[i];
int tmpY = y + dy[i];
// 是通路
if (Map[tmpX][tmpY] == '.')
{
// 未到边界
if (tmpX >= 1 && tmpX <= n && tmpY >= 1 && tmpY <= m)
{
// 未访问
if (vis[tmpX][tmpY] == false)
{
// 标记已经走过
vis[tmpX][tmpY] = true;
// 存储路线
ansx[pos] = tmpX;
ansy[pos] = tmpY;
pos++;
// 递归
dfs(tmpX, tmpY);
// 回溯
vis[tmpX][tmpY] = false;
pos--;
}
}
}
}
}
int main()
{
// 输入
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> Map[i][j];
}
}
// dfs
vis[1][1] = true;
dfs(1, 1);
return 0;
}