深度优先搜索(DFS) 结合洛谷习题
文章目录
前言
当处于一个巨大的迷宫中,你只有第一人称视角,应该如何以最快速度走出这个迷宫呢?
依据深度优先算法,这种策略一定有效:以当前位置为起点,沿着一条路向前走。当遇到一个岔路口时,就选择一个岔路前进,如果这个岔路是死胡同,就退回上一个岔路口;如果这个岔路又有新的岔路口,就依据之前的做法,再选择一条岔路口继续前进。
这样就出现了一个问题,当经历了众多岔路口之后,你的面前出现了死胡同,你的记忆力已经不支持你退回一开始的岔路口,那岂不是又要在迷宫里打转转了?其实解决这个问题的方法很简单,无论是选择岔路口或者是返回上一个岔路口,只要保证在走的过程中你的右手一直贴着右边的墙壁,你就一定会走出迷宫。
这种不撞南墙不回头的策略,形象地从深度上彻底挖掘了这个迷宫,因此这种搜索路径的方法被称作深度优先搜索,也称为DFS(Depth-First-Search)
一、深度优先搜索是什么?
采用递归的方法,先沿一条路搜到底,在递归回上一个节点,沿另一个方向搜索,以此类推。这就是广义上的深搜。
正如图上所示,运用深搜的思路,我们会沿着数字的顺序进行搜索,即数字的顺序就是搜索的路径。
首先,我们会从起点->1->2->3,此时到3已经无路可走,我们就要退回删一个节点2->4,此时4已经无路可走,退回5->6,退回5->7以此类推。
最终,我们就遍历了一次所有的节点,于此同时,也找到了通往各节点的路径。
到此可能你对深搜还是一知半解,没关系,跟随着下面的例题
一起进入深搜的应用,加深对深搜的理解!
二、洛谷例题
1.P1706全排列问题
题目描述
输出自然数 1到 n所有不重复的排列,即 n*n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n。
输出格式
由 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 55 个场宽。
输入输出样例
输入 #1
3
输出 #1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
题解:
思路:
以n=3举例:
首先,分别建立两个数组,一个int型的数组a,用于记录每种情况,一个bool型数组vis,用于判断当前数字是否使用过。
之后,编写dfs函数:先判断当前的vis[i]是否为0,如果为0则说明未被访问过,则进入递归程序。(具体实现在代码解读)
最后,如果当前搜索层数大于n时,退出递归。
#include<iostream>
#include<iomanip>
using namespace std;
int a[20];
bool vis[20];
int n;
void print()
{
for(int i=1;i<=n;i++)
{
cout<<setw(5)<<a[i];//setw()指定宽度
}
cout<<endl;
}
void dfs(int x)
{
if(x>n) print();
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
a[x]=i;
dfs(x+1);
vis[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
}
代码解读:
//递归(dfs)主体
if(!vis[i])
{
vis[i]=1;//未访问过的现在要标记已访问
a[x]=i;//注意是a[x],x代表当前搜索的层数,在当前这一层选一个数i
dfs(x+1);//继续往下一层搜索
vis[i]=0;//搜索完之后,要释放上一层的数,以便下一次使用
}
2.P1605 迷宫
题目背景
给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
题目描述
无
输入格式
第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。
输出格式
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。
输入输出样例
输入 #1
2 2 1
1 1 2 2
1 2
输出 #1复制
1
说明/提示
【数据规模】
1≤N,M≤5
题解
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,t,sx,sy,fx,fy,ans;
bool vis[10][10];
bool mp[10][10];
int Xx[]={1,0,-1,0};
int Yy[]={0,-1,0,1};
void dfs(int x,int y)
{
if(x==fx&&y==fy)
{
ans++;
return;
}
for(int i=0;i<4;i++)
{
int dx=Xx[i]+x;
int dy=Yy[i]+y;
if(dx>=1&&dx<=n&&dy>=1&&dy<=m&&!vis[dx][dy]&&!mp[dx][dy])
{
vis[x][y]=1;
dfs(dx, dy);
vis[x][y]=0;
}
}
}
int main()
{
cin>>n>>m>>t;
cin>>sx>>sy>>fx>>fy;
while(t--)
{
int x,y;
cin>>x>>y;
mp[x][y]=1;
}
dfs(sx,sy);
cout<<ans;
return 0;
}