图论——————DFS or BFS

DFS 深度优先搜索

 深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。
 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过
 或者在搜寻时结点不满足 条件,搜索将回溯到发现节点v的那条边的起始节点。整个进
 程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。
搜索过程

在这里插入图片描述

  1. 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;
}

洛谷-p1065迷宫

思路过程

  1. 在起点的位置,依次遍历上下左右的方向,把访问过的点 标记一下,(如果遇到障碍或者标记过的点 就回溯)
  2. 在下一个点就重复第一步, 直到到达终点。(这时候就把答案+1);
  3. 到达终点也重新回溯一下,寻找一下新的路径。

代码实现

#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=1do {
        指针head后移一位,指向待扩展结点;
        for (int i=1;i<=max;++i) {             //max为产生子结点的规则数 
        if (子结点符合条件) {
                tail指针增1,把新结点存入列尾;
                if (新结点与原已产生结点重复) 删去该结点(取消入队,tail减1;
                else if (新结点是目标结点) 输出并退出;
            }
        }
    } while(head < tail);//队列为空
}

搜索过程

在这里插入图片描述

洛谷-p1746离开中山路

代码实现

#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();
}

洛谷-p1137 奇怪的电梯

代码实现

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值