DFS 深度优先搜索
深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。
沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过
或者在搜寻时结点不满足 条件,搜索将回溯到发现节点v的那条边的起始节点。整个进
程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。
搜索过程
- p1706全排列问题~链接
做题思路
- 构造一个全排列的树 把所有可能排列的情况表示出来 如下图
- 构造一个全排列的树 把所有可能排列的情况表示出来 如下图
-
开一个数组d[i]装排列的数字,把d[]数组里面的数字标记一下,这样搜索到重复的数字就跳过。
-
接下来 只要给定特定的条件来遍历这个树就行了。
程序实现
- 封装一个输出函数,当达到条件的时候就输出一下;
- 在封装一个查询函数,从d[i] 数组第一个格子开始选择,把没有标记过的数字填进来 开始选择下一个格子,就递归的应用 ,在递归结束后 把标记过的数字 重新更新为没有标记过的,这样在回溯的时候才能够重新选择。
代码实现
#include<iostream>
using namespace std;
int n,f[100],dis[100];//f是判断是否用过这个数
void print()//输出函数
{
int i;
for(i=1;i<=n;i++)
printf("%5d",d[i]);//保留五位常宽
cout<<endl;
}
void dfs(int k)//搜索,当前是第k格
{
int i;
if(k==n) //填满了的时候
{
print();//输出当前解
return;
}
for(i=1;i<=n;i++)// 循环所有的数
{
if(!f[i])//如果当前数没有用过
{
f[i]=1;//标记一下
d[k+1]=i;//把这个数填入数组
dfs(k+1);//填下一个
f[i]=0;//回溯 这是是重点 没有这个就不能够在下一个的时候重新选着 就比如 输出123了 从321 开始的时候不会选到1
}
}
}
int main()
{
cin>>n;
dfs(0);//注意,这里是从第0格开始的!
return 0;
}
思路过程
- 在起点的位置,依次遍历上下左右的方向,把访问过的点 标记一下,(如果遇到障碍或者标记过的点 就回溯)
- 在下一个点就重复第一步, 直到到达终点。(这时候就把答案+1);
- 到达终点也重新回溯一下,寻找一下新的路径。
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
bool G[15][15],VIS[15][15];//G为总地图,VIS记录是否访问
int n,m,d[5]={-1,0,1,0,-1};//方向 (-1,0)~左 (0.1)~右 类推
int nx,ny,ex,ey,CNT;
//nx,ny起点坐标;ex,ey终点坐标,CNT路径条数
void dfs(int x,int y)
{
if (x ==ex&&y ==ey)//如果到终点
{
CNT++;//路径加一
return;//回去继续查找
}
for (int k=0;k<4;k++)
{
int l=x+d[k];int r=y+d[k+1];
if (l>=1&&r>=1&&l<=n&&r<=m&&!G [l][r]&&!VIS [l][r])
{
VIS [l][r]=true;//标记为已访问
dfs (l,r);
VIS [l][r]=false;//回溯
}
}
return;
}
int main ()
{
int t,zx,zy;
cin>>n>>m>>t>>nx>>ny>>ex>>ey;
G[nx][ny]=true; //这就是许多人(我)40分的原因
//因为dfs函数里并没有将起点设为已访问
//所以在后面的访问里,可能访问起点许多次
//所以你的答案可能比标准答案多
while(t--)
{
cin>>zx>>zy;
G[zx][zy]=true;//设为障碍
}
dfs (nx,ny);//从起点开始寻找
cout<<CNT;
return 0;
}
BFS 广度优先搜索
它是从初始结点开始,应用产生式规则和控制策略生成第一层结点,同时检查目标结点
是否在这些生成的结点中。若没有,再用产生式规则将所有第一层结点逐一拓展,
得到第二层结点,并逐一检査第二层结点是否包含目标结点。若没有,再用产生式
规则拓展第二层结点。如此依次拓展,检査下去,直至发现目标结点为止。如果拓
展完所有结点,都没有发现目标结点,则问题无解。
所有结点的拓展都遵循“先进先出”的原则,所以采用“队列”来存储这些状态。宽度优先搜索的算法框架如下:
void bfs() {
初始化,初始状态存入队列;
队列首指针head=0; 尾指针tail=1;
do {
指针head后移一位,指向待扩展结点;
for (int i=1;i<=max;++i) { //max为产生子结点的规则数
if (子结点符合条件) {
tail指针增1,把新结点存入列尾;
if (新结点与原已产生结点重复) 删去该结点(取消入队,tail减1);
else if (新结点是目标结点) 输出并退出;
}
}
} while(head < tail);//队列为空
}
搜索过程
代码实现
#include<cstdio>
int sx,sy,ex,ey,n;//sx,sy是起点坐标,ex,ey是终点坐标
char a[1100][1100];//表(输入没有空格得用char读)
struct lol{int x,y,t;} d[1100000];//记录每次要跑的点
int dx[4]={0,1,0,-1};//x的偏移量
int dy[4]={1,0,-1,0};//y的偏移量 (两偏移量组成,上,下,左,右)
void dfs()
{
int tou=1,wei=2;//tou记录当前点,wei记录当前点走的点
d[tou].x=sx;d[tou].y=sy;d[tou].t=0;//把初始点记录下来,把初始步刷0
while(tou!=wei)
{
for(int i=0;i<4;i++)//四个方向
{
int x=d[tou].x+dx[i];// 当前点x加偏移量
int y=d[tou].y+dy[i];// 当前点y加偏移量
if(x==ex&&y==ey){printf("%d",d[tou].t+1);return;}//到终点输出
if(x>=1&&x<=n&&y>=1&&y<=n&&a[x][y]=='0')//如果当前点走的点没超n记录当前点走的点
{
a[x][y]='1';//封点
d[wei].x=x;d[wei].y=y;d[wei].t=d[tou].t+1;wei++;
}
}tou++;
}
}
int main()
{
scanf("%d",&n); //输入
for(int i=1;i<=n;i++)
{
scanf("%s",a[i]+1);//不能用%c,%c会读回车
}
scanf("%d %d %d %d",&sx,&sy,&ex,&ey);//输入起点,终点
a[sx][sy]=1;//封起始点
dfs();
}
代码实现
#include<iostream>
using namespace std;
int a[10010];//记录每层楼的数字
int b[10010];//判重
int q[10010];//数组模拟队列
int ans[10010];//记录答案
int n,xx,yy,sum;
int main()
{
ios::sync_with_stdio(false);//加快流输入输出
cin>>n>>xx>>yy;
if(xx==yy)//特判一手
{
cout<<"0";
return 0;
}
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
if(sum<yy-xx)//再判一手
{
cout<<"-1"<<endl;
return 0;
}
int head=0,tail=1,x,y;
b[xx]=true;
q[1]=xx;
do
{
head++;
x=q[head]+a[q[head]];
if(x<=n&&!b[x])//假如可以入队,就入队
{
tail++;
q[tail]=x;
b[x]=true;//防止重复
ans[tail]=ans[head]+1;//广搜记录答案路径
}
if(x==yy)//搜到就输出
{
cout<<ans[tail];
return 0;
}
x=q[head]-a[q[head]];
if(x>0&&!b[x])//同上
{
tail++;
q[tail]=x;
b[x]=true;
ans[tail]=ans[head]+1;
}
if(x==yy)
{
cout<<ans[tail];
return 0;
}
}while(tail>head);
cout<<"-1"<<endl;//假如没有输出-1,注意要输出换行符
return 0;
}