迪杰斯特拉的堆优化

前序:

之前自己写过朴素版本的迪杰斯特拉,这里再补充一个优化版本的。

题目:

(双向图版本)

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

LOST GRACE DISCOVERED

史东薇尔城坐落于宁姆格福与利耶尼亚湖的交界处,地势险要、易守难攻,是连接南北重要的交通枢纽。史东薇尔城不仅地理位置重要,内部更是错综复杂,第一次来到这里的褪色者往往会迷路。魔法师MaverickFW接手了大量的来自史东薇尔城的任务委托,每一次执行任务MaverickFW都需要从自己当前的所在地去往史东薇尔城再跑去任务地点。我们将地图简化为一张无向图,而史东薇尔城是一号结点。MaverickFW想知道自己每次任务最短需要跑多远,他每次会告诉你自己当前所在结点与任务地所在结点,希望你能准确回答他。

输入描述:

第一行两个正整数n,m表示结点数与边数

接下来m行,每行三个正整数vi,wi,di,表示vi到wi​的距离为di

接下来一行一个整数T,表示询问次数

之后T行,每行两个正整数si,ti表示MaverickFW的出发点与任务点

输出描述:

对于每一个询问输出一行一个正整数表示最短距离

示例1

输入

3 3
1 2 1
1 3 1
2 3 1
3
1 3
2 3
1 2

输出

1
2
1

代码:

#include "iostream"
#include "queue"
#include "vector"
#include "cstring"

using namespace std;

const int N=1e6+9;
typedef long long LL;
typedef pair<int,int> PI;

int to[N],val[N],nex[N],head[N],n,m,cnt=0;
LL dis[N];

void add(int a,int b,int c){
	to[++cnt]=b;
	val[cnt]=c;
	nex[cnt]=head[a];//nex是针对与点来说的,看这个点相连接的下一条边是什么 
	head[a]=cnt;
}

void DJS(){
	bool vis[N];
	priority_queue<PI,vector<PI>,greater<PI>> tool;
	tool.push({0,1});
	while(!tool.empty()){
		PI temp=tool.top();
		tool.pop();
		int now=temp.second;
		if(vis[now])//这里一定要注意加上是否访问过这个点了 
			continue;
		vis[now]=true;
		for(int i=head[now];i;i=nex[i]){//这里需要小心nex与to的区别 
			int j=to[i];
			dis[j]=min(dis[j],dis[now]+val[i]);
			tool.push({dis[j],j});
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
		add(b,a,c);//题目中说的是双向图,所以需要添加两次边 
	}
	memset(dis,0x3f3f3f,sizeof(dis));
	dis[1]=0;
	DJS();
	int times,x,y;
	cin>>times;
	while(times--){
		cin>>x>>y;
		cout<<dis[x]+dis[y]<<endl;
	}
	return 0;
}
 

 

主要依靠堆优化来完成,正常的迪杰斯特拉,可能1e3或者1e4就是极限了,但是优化版本的可以直接提升到1e6的数量级,因为时间复杂度为O(Nlog(N)),所以大大减小了。

简要的思想便是,通过邻接表存储边的信息!这个存储过程背下来就是了(如果实在记不清的话,然后多打几遍,就知道是什么意思了,to和nex数组是针对不同的东西的,to是指下一个点是什么,nex是说from点所连接的下一条边是什么,很重要!)然后使用priority_queue来记录顺序,每一次取顶上的元素就好了。

最后由于这个是双向图的版本,所以在add的时候,需要add(a,b,c)与add(b,a,c)两个情况都需要加上!

最后在循环所有的边和查看vis数组的时候,联想到之前朴素版本的情况就可以很简单的更新好整个过程!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值