DFS(深度优先搜索)
基本概念
深度优先搜索(Depth First Search)一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(n!)以上。
如果找到多个,则任选一个顶点,然后继续从该顶点出发;
如果一个都没有找到,则回退到之前访问过的顶点,看看是否有漏掉的;
深搜总结起来就是:
(1)先想好递归方程
(2)处理好枚举数据,对已遍历的数据要标记
(3)一定一定要还原现场,这是回溯的决定性条件
算法思想
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的方法为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
DFS模板
void dfs()//参数用来表示状态
{
if(到达终点状态)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态)
return;
if(特殊状态)//剪枝
return ;
for(扩展方式)
{
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
标记;
dfs();
(还原标记);
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
DFS一般用于求解问题有多少种情况,多少条路径,最大路径等等。
例题
#include<bits/stdc++.h>
using namespace std;
int n,a[10],q[50000],v[10],p;
void dfs(int index)//选择第step位置的数
{
if(index==n+1)//第n+1个位置时,表示前面已经选择了n个数
{
for(int i=1; i<=n; i++)//进行输出
{
printf("%5d",q[i]);//%5d是保留五个场宽
}
cout << '\n';
return;//返回上步继续选择
}
for(int i=1; i<=n; i++)//遍历所有可选择的数字
{
if(v[i]==0)//满足未被使用的条件
{
q[index]=a[i];//选择数字i,放入数组q
v[i]=1;//将数字i进行标记
dfs(index+1);//进行下步选择
v[i]=0;//撤销数字i的标记
}
}
return ;
}
int main()
{
cin >>n;
for(int i=1; i<=n; i++)a[i]=i;
dfs(1);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,m,t,a[10][10],v[10][10],ans;
int d[][2]={{0,1},{1,0},{0,-1},{-1,0}};
struct point
{
int x,y;
};
point start,fi;
void dfs(point s)//选择在s点下步的点
{
point temp;
if(s.x==fi.x&&s.y==fi.y)//到达终点,可达路径加一
{
ans++;
return;//返回上个点
}
for(int i=0;i<4;i++)//遍历每个点的四个方向
{
temp.x=s.x+d[i][1];//四个方向的移动
temp.y=s.y+d[i][0];
if(temp.y>=1&&temp.y<=n&&temp.x>=1&&temp.x<=m&&a[temp.y][temp.x]==0&& v[temp.y][temp.x]==0)//若下个点在迷宫范围内且为没有被访问过的非障碍点
{
v[temp.y][temp.x]=1;//标记选择的点,防止重复遍历
dfs(temp);//以选择的点进行新的遍历
v[temp.y][temp.x]=0;//撤销标记
}
}
return;
}
int main()
{
cin >> n >> m>> t;
cin >> start.x >> start.y >> fi.x >> fi.y;
point temp;
for(int i=0;i<t;i++)
{
cin >> temp.x >> temp.y;
a[temp.y][temp.x]=1;
}
v[start.y][start.x]=1;
dfs(start);
cout << ans;
return 0;
}
BFS(广度优先搜索)
BFS(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和广度优先搜索类似的思想。属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
简单来说,BFS是一种图搜索的算法,目的是用于搜索检查每一个可以达到的点,直到找到结果为止。他和DFS的区别:DFS是一条路走到黑,如果没用路就返回,而BFS是从上往下层次依序遍历,我们一般用队列来实现BFS的遍历。
算法步骤:
1. 首先将根节点放入队列中。
2. 从队列中取出第一个节点,并检验它是否为目标。
如果找到目标,则结束搜寻并回传结果。
否则将它所有尚未检验过的直接子节点加入队列中。
3. 若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传"找不到目标"。
4. 重复步骤2。
BFS模板
struct node //结构体用于保存每一状态信息
{
......
};
void bfs()
{
queue<node> Q; // 定义存放结构体的队列Q
起点入队
标记起点
while(!Q.empty()) // 队非空
{
node u=Q.front(); //获取队首信息(结构体)
Q.pop();//每次从队首把所有可能的状态走完,队首要出队
for(拓展接下来所有可能的状态)
{
得到并记录新的状态信息
判断状态是否合法
若合法
{
当前标记为已访问
Q.push(合法节点);//状态入队
判断是否到达目标
若满足,输出答案,return ;
}
}
}
}
BFS一般用于解决最短路径,最短步骤等最优问题。
例题
#include <bits/stdc++.h>
using namespace std;
int a, b, c, d, n, vis[1005][1005];
int dc[] = {0, 0, -1, 1};
int dr[] = {-1, 1, 0, 0};
char s[1005][1005];
struct point//当前的位置结构体
{
int c, r, step;
};
int bfs(point start)
{
queue<point> q;
q.push(start);//起点入队
vis[start.r][start.c] = 1;//标记起点
while (!q.empty())
{
point front = q.front(),p;//获取队首元素
q.pop();//队首元素出队
for (int i = 0; i < 4; i++)//遍历队首元素的所有可能
{
p.r = front.r + dr[i];
p.c = front.c + dc[i];
p.step = front.step + 1;//得到一个新的点,步数加一
if (p.r >= 0 && p.r <= n && p.c >= 0 && p.c <= n && s[p.r][p.c] == '0' && vis[p.r][p.c] == 0)//判断是否在迷宫内且到访问
{
vis[p.r][p.c] = 1;//标记这个点
q.push(p);//将这个点入队
}
if (p.r == c && p.c == d)
{
return p.step;
}
}
}
return 0;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> s[i][j];
}
}
cin >> a >> b >> c >> d;
point s;
s.r = a;
s.c = b;
s.step = 0;
cout << bfs(s);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,a,b,s[205],visited[205];
struct node
{
int id,step;
};
int bfs(node start)
{
queue<node> q;
q.push(start);//将起始楼层入队
visited[start.id]=1;//标记楼层
while (!q.empty())
{
node front=q.front(),down,up;//获取现在所在楼层的状态
down.id=front.id-s[front.id];//当前楼层向下可到达的楼层
up.id=front.id+s[front.id];//当前楼层向上课到达的楼层
down.step=front.step+1;//步数加一
up.step=front.step+1;
if (front.id==b)//如果当前楼层为目标楼层,返回结果
{
return front.step;
}
if (down.id>=1 && visited[down.id]==0)//判断当前楼层向上是否是可达的
{
visited[down.id]=1;
q.push(down);//入队
}
if (up.id<=n && visited[up.id]==0)//与上相同
{
visited[up.id]=1;
q.push(up);
}
q.pop();//在遍历了所有可能后,将队首元素出队
}
return -1;
}
int main()
{
cin >> n >> a >> b;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
node s;
s.id=a;
s.step=0;
cout << bfs(s);
return 0;
}