7-4 走迷宫II(Dijkstra 算法)

今天学到了几个关于图最短路的算法,一个就是Dijkstra 算法,还有一个是Floyd 算法,这道题由于要开门所以我们从每一个点向其他点移动时,花费的时间不一定是1分钟了。所以对于单路径且贬值不同的情况下,就得用Dijkstra 算法了。我们先将该题读入读入后就开始算法详解。

读入过程如下

int bx,by,ex,ey;其中bx,by是起点的位置,ex,ey是终点的位置
 while(cin>>n>>m)
 {
 	for(int i=0;i<n;i++)
 	{
 		for(int i1=0;i1<m;i1++)
 		{
 		 cin>>mg[i][i1];
		  if(mg[i][i1]==3)
		  {
		  	bx=i;by=i1;
		  }
		  if(mg[i][i1]==4)
		  {
		  	ex=i;ey=i1;
		  }
		}
	}

之后相当于一个建树过程,跟第一题一样,我们将迷宫中的每一个点具体化,就是数字化取代坐标化,这样比较方便一点。

我们将每一个结点可以到达的结点位置和距离用一个奇妙的方法保存下来,具体如下。

    int begin=bx*m+by;开始结点的数字化
	int end=ex*m+ey;终点结点的数字化
	vector<vector<cao> >mig(n*m+10,vector<cao>(0));就是这个方法,我们将该结点能去的位置和距离绑在一起,所以所以在前面有一个typedef pair<int,int>cao;
	for(int i=0;i<n;i++)
	{
		for(int i1=0;i1<m;i1++)
		{
			
			if(mg[i][i1]==1)continue;如果到不了的话就继续下一次。
			int sta=i*m+i1;将每一个起点都数字化
			for(int i2=0;i2<4;i2++)
			{
				int ux=i+work[i2][0];偏移结点1
				int uy=i1+work[i2][1];偏移结点2
				if (ux<0||ux>=n||uy<0||uy>=m||mg[ux][uy]==1)跑出边缘就出去
                    continue;
				int fin=ux*m+uy;到达偏移位置
				if(mg[ux][uy]==2)如果该结点的位置是2的话就是花费时间为2
				{
					mig[sta].push_back({fin,2});从sta这个点到fin的点花了2分钟
				}
				else 其余就是0和4了都是1了
				{
					mig[sta].push_back({fin,1}); 从sta这个点到fin的点花了1分钟
				}
			}
		}
	} 

以上属于一个始初化,接下来就是我们Dijkstra 算法的具体内容了。

如果不会的话推荐一个视频,还可以私信我。

以下链接是为了各位学习算法

因陪女朋友逛街,荣获图灵奖——Dijkstra算法_哔哩哔哩_bilibili

对于此算法我们采取优先队列形式的就是将花费时间最小的结点放在队列头部的方法,之后以该结点为起点,将这个队列头能到达的位置和距离分别入队,并且我们会额外创建两个数组一个储存那个最短距离或时间数,一个存储是不是遍历过这个数组,就是判断是不是已这个点为起点遍历过,当一个点做为起点跑完之后就,让他出队。

具体代码和解释如下

    vector<int>bol(n*m+10,0);判断对应点是否做为起点跑过
	vector<int>dis(n*m,1e9);存储从起点到对应点的最小时间数
	dis[begin]=0;始初化,起点时间为0
	priority_queue<cao,vector<cao>,greater<cao> >you;优先队列,将那个最小距离数始终排在队首
	you.push({0,begin});将起点入队
	while(!you.empty())算法结束条件
	{
		auto li=you.top();取那个最小距离的那个头
		you.pop();每一次的出栈操作
		if(bol[li.second])continue;如果遍历过了就是进入下一个循环
		bol[li.second]=1;作为起点遍历过了就赋值为1
		int o=li.second;我们将距离跟结点绑在一起,第二个为当前起点(这个当前起点不一定是最初起点)
		
	    for(auto op:mig[o])我们在之前把当前起点对应的点和距离绑在一起储存在一个空间里
	    {
	    	if(dis[op.first]>dis[o]+op.second)这一步就是此算法的核心要好好理解理解
	    	{
	    		dis[op.first]=dis[o]+op.second;这个dis[o]存的是从起点到该点的最小距离,
如果从起点到当前位置的距离大于从起点到上一个点的距离加上上一个点到这一个点的距离,
这个时候我们就可以将当前从最初起点到当前点的距离位置更新为这个更小的值,并将其入队。
	    		you.push({dis[op.first],op.first});
			}
		}
	} 

这道题的核心就说完了,接下来就是最终代码了。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>cao;
int work[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int mg[110][110];
int n,m;
int main()
{
 int bx,by,ex,ey;
 while(cin>>n>>m)
 {
 	for(int i=0;i<n;i++)
 	{
 		for(int i1=0;i1<m;i1++)
 		{
 		 cin>>mg[i][i1];
		  if(mg[i][i1]==3)
		  {
		  	bx=i;by=i1;
		  }
		  if(mg[i][i1]==4)
		  {
		  	ex=i;ey=i1;
		  }
		}
	}
	int begin=bx*m+by;
	int end=ex*m+ey;
	vector<vector<cao> >mig(n*m+10,vector<cao>(0));
	for(int i=0;i<n;i++)
	{
		for(int i1=0;i1<m;i1++)
		{
			
			if(mg[i][i1]==1)continue;
			int sta=i*m+i1;
			for(int i2=0;i2<4;i2++)
			{
				int ux=i+work[i2][0];
				int uy=i1+work[i2][1];
				if (ux<0||ux>=n||uy<0||uy>=m||mg[ux][uy]==1)
                    continue;
				int fin=ux*m+uy;
				if(mg[ux][uy]==2)
				{
					mig[sta].push_back({fin,2});
				}
				else 
				{
					mig[sta].push_back({fin,1}); 
				}
			}
		}
	} 
	vector<int>bol(n*m+10,0);
	vector<int>dis(n*m,1e9);
	dis[begin]=0;
	priority_queue<cao,vector<cao>,greater<cao> >you;
	you.push({0,begin});
	while(!you.empty())
	{
		auto li=you.top();
		you.pop();
		if(bol[li.second])continue;
		bol[li.second]=1;
		int o=li.second;
		
	    for(auto op:mig[o])
	    {
	    	if(dis[op.first]>dis[o]+op.second)
	    	{
	    		dis[op.first]=dis[o]+op.second;
	    		you.push({dis[op.first],op.first});
			}
		}
	} 
    if(dis[end]>1e9/2)cout<<"unreachable"<<endl;
    else cout<<dis[end]<<endl;
 }
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

B程洪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值