思想
从起点出发,走过的点做上标记,若发现有没走过的点就随意挑一个往前走,无路可走就回退
伪代码模板
Dfs(v)
{
if(v是旧点或满足结束条件)
return;
对和v相邻的每个点U如果满足条件(迷宫问题对应着上左下右方向)
{
将v标记为旧点;
Dfs(U);
取消标记
}
}
int main(){
将所有点都标记为新点
Dfs(v); //v为起始点
return 0;
}
经典例题
🚩1.迷宫问题
我们用一个二维数组表示一个迷宫
....S*
.***..
.*..*.
*.***.
.T....
//n=5,m=6
其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地,你需要从S出发走到T,每次只能向上下左右相邻的位置移动,不能走出地图,也不能穿过墙壁,每个点只能通过一次,输出一种从起点到终点的走法
思路:我们可以用二维数组表示地图,然后用dfs来查找是否能找到终点
#include<bits/stdc++.h>
using namespace std;
int n,m;
char maze[110][110]; //地图记录每个位置
bool visited[110][110]; //记录当前位置是否已经走过
bool in(int x,int y) //判断坐标x和y是否在地图内
{
return 0<=x && x<n && 0<=y && y<m;
}
bool dfs(int x,int y)
{
if(maze[x][y]=='T') //若遇到出口返回true
{
return true;
}
visited[x][y]=1; //标记当前位置为已访问
maze[x][y]='m'; //用m来表示走过了这个位置方便输出查看路线
//尝试向上走
int tx=x-1,ty=y;
if(in(tx,ty)&&maze[tx][ty]!='*'&&!visited[tx][ty]) //如果可以向上走,下面类似
{
if(dfs(tx,ty))
return true;
}
//尝试向左走
tx=x,ty=y-1;
if(in(tx,ty)&&maze[tx][ty]!='*' && !visited[tx][ty])
{
if(dfs(tx,ty))
return true;
}
//尝试向下走
tx=x+1,ty=y;
if(in(tx,ty)&&maze[tx][ty]!='*' && !visited[tx][ty])
{
if(dfs(tx,ty))
return true;
}
//尝试向右走
tx=x,ty=y+1;
if(in(tx,ty)&&maze[tx][ty]!='*' && !visited[tx][ty])
{
if(dfs(tx,ty))
return true;
}
//取消标记
visited[x][y]=0;
maze[x][y]='.';
return false;
}
int main(){
cin>>n>>m;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin>>maze[i][j];
}
}
int x,y;
//找到起始点S的坐标
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if(maze[i][j]=='S')
x=i,y=j;
}
}
memset(visited,false,sizeof(visited)); //设置初始值为false,避免出问题
//对起始点S调用dfs函数,若找到路则输出结果
if(dfs(x,y))
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cout<<maze[i][j];
}
cout<<endl;
}
}
else
{
cout<<"NO!"<<endl;
}
return 0;
}
我们可以看到尝试上左下右有点重复,我们可以用一个二维数组int dir[4][2]= {{-1,0},{0,-1},{1,0},{0,1}};来表示如果去上左下右x和y的增量,然后用一个for循环来优化代码
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}}; //表示分别向上左下右(逆时针)方向x和y的增量
//尝试上左下右
for (int i = 0; i < 4; i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&& maze[tx][ty]!='*' && !visited[tx][ty])
{
if(dfs(tx,ty))
return true;
}
}
🚩2.迷宫最短路径问题(最短路径问题更适合用bfs)
在上面题目的基础上不求可以到达的一种路径而是求最短路径的长度
#include<bits/stdc++.h>
using namespace std;
int n,m;
char maze[110][110]; //地图记录每个位置
bool visited[110][110]; //记录当前位置是否已经走过
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int res=10000000;
bool in(int x,int y) //判断坐标x和y是否在地图内
{
return 0<=x && x<n && 0<=y && y<m;
}
void dfs(int x,int y,int step) //step表示当前路径长度
{
if(maze[x][y]=='T') //达到结束条件
{
if(step<res)
{
res=step;
}
return;
}
visited[x][y]=true; //标记当前位置为已访问
for (int i = 0; i < 4; i++) //尝试每个方向
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&&maze[tx][ty]!='*'&&!visited[tx][ty])
{
dfs(tx,ty,step+1);
}
}
//取消标记,因为要把所有路径都找出来
visited[x][y]=0;
}
int main(){
cin>>n>>m;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin>>maze[i][j];
}
}
int x,y;
//找到起始点S的坐标
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if(maze[i][j]=='S')
x=i,y=j;
}
}
memset(visited,false,sizeof(visited));
dfs(x,y,0);
cout<< res <<endl;
return 0;
}
🚩3.n皇后问题
在这里是记录另一种解法,在递归中相当于n重循环逐次判断,这里可以用左斜bool数组和右斜数组和列数组来判断下行放的位置可不可以放
代码基本直接套模板,不过要注意判断左斜条件要加n,因为右上角行数-列数是负值所以加上n让他为正,这样左斜索引取值就是0~n-1
#include<bits/stdc++.h>
using namespace std;
int res,n;
bool cols[100],zuoxie[100],youxie[100];
bool check(int k,int i)
{
return !cols[i] && !zuoxie[k-i+n] && !youxie[k+i];
}
void dfs(int k)
{
if(k==n+1) //结束条件
{
res++;
return;
}
for (int i = 1; i <= n; i++)
{
if(check(k,i))
{
cols[i]=true;
zuoxie[k-i+n]=true;
youxie[k+i]=true; //以上3行是加标记
dfs(k+1);
cols[i]=false; //下面3行是取消标记
zuoxie[k-i+n]=false;
youxie[k+i]=false;
}
}
}
int main()
{
cin>>n;
dfs(1);
cout<<res<<endl;
}