优先队列+狄克斯特拉 求单源最短路

优先队列+狄克斯特拉 求单源最短路

先上模板:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAX 100005
#define MAXN ~(1<<31)
#define pb push_back
#define INF 0x3f3f3f3f
 
priority_queue<pair<int,int> > p;
int color[MAX];
int d[MAX];
int n;
void dijkstra(int t)
{
	for(int i=1;i<=n;i++)
	{
		d[i]=INF;
		color[i]=0;
	}
	d[t]=0;
	p.push(make_pair(0,t));
	color[t]=1;
	while(!p.empty())
	{
		pair<int,int> f=p.top();p.pop();
		int u=f.second;
		color[u]=2;
		if(d[u]<f.first*(-1)) continue;
		for(int j=0;j<s[u].size();j++)
		{
			int v=s[u][j].first;
			if(color[v]==2)continue;
			if(d[v]>d[u]+s[u][j].second)
			{
				d[v]=d[u]+s[u][j].second;
				p.push(make_pair(d[v]*(-1),v));
				color[v]=1;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<abs(d[i]==INF?MAXN:d[i])<<' '; 
	}
	cout<<endl;
}
int main()
{
	ios::sync_with_stdio(false); 
	int m,s1;
	cin>>n>>m>>s1;
	while(m--)
	{
		int u,v,w;
		cin>>u>>v>>w;
		s[u].push_back(make_pair(v,w));
	}
	dijkstra(s1);
	return 0;
}

垃圾图论,毁我青春! 图论大法好!
在某次比赛后,我发现我图论的知识盲区有点多,所以下定决心,好好学图论!
之后就陷入自闭! 好好学习!自闭!的无限循环,终于在某一天的下午,一觉起来,恍然大悟!哈哈哈哈哈 !
我不会告诉你我还是什么都不懂的


下面进入正题:
在聊 优先队列版的狄克斯特拉求最短路 之前,我们先要聊聊 普通版本的狄克斯特拉 是什么原理。
其实用我的话说就是条条大路通罗马,一路更比一路短。
什么意思呢?举个例子:
有三个点A,B,C。
A --> B 的距离为 2
B --> C 的距离为 3
A --> C 的距离为 6
显而易见 如果直接选择A --> C 直达,那么其距离大于 A --> B --> C 的距离。
所以狄克斯特拉的算法精髓就是找到这么几个中转站,使得两地的距离最短。
而如果要实现这种想法,还要运用贪心。
我们先设立两个集合,第一个集合是原图,第二个集合是已选的点和备选的点,运用贪心 找到备选的点中路最短的那条,再重复上述步骤。
//
图论的东西很难用言语表达出来,以后会考虑录视频聊,现在就当是我和我自己尬聊吧。ggggggg
//


知道普通的狄克斯特拉求单源最短路劲之后,就要考虑加速和处理更大的数的问题了。
这里我们选择的是使用优先队列来配合狄克斯特拉求最短路劲。
下面是我看到的写的比较好的一篇聊优先队列的文展:
优先队列priority_queue


1.1 存图的方式

vector<pair<int,int> > s[MAX];	

对于加权图,最原始的方式就是通过二维数组存储点与点之间的联系,但这样的建图会在数据达到1e5的时候爆炸,所以这里就有了一个更好的存图方式。

即利用vector<pair<int,int>>s[100]来建立它们之间的联系。

其结构组成为:
s[u].first 与u链接的点
s[u].second 与u链接的点的权值

则建图的code如下:

	while(m--)
	{
		int u,v,w;
		cin>>u>>v>>w;
		s[u].push_back(make_pair(v,w));
	}

2.1 优先队列存图

priority_queue<pair<int,int> > p;

我们可以将选好的第一个点放入优先队列p,再理由优先队列的性质,可以保证每次取出的top是所有路劲的最小值。
但这里需要注意一点:默认情况下的优先队列是从大到小的,如果要选取最小的数,就要再*-1。

3.1 核心步骤+注释

	while(!p.empty())
	{
		pair<int,int> f=p.top();p.pop();//每次取出的数一定是最短路径的点/其中 first 指向 权值,即路径的长短,second 指向点
		int u=f.second;
		color[u]=2;//标记该点经过
		if(d[u]<f.first*(-1)) continue;//如果在之前找到了比它还小的最短路,就continue
		for(int j=0;j<s[u].size();j++)//这一步的目的就是将与u有联系的点压入队列中
		{
			int v=s[u][j].first;
			if(color[v]==2)continue;//如果该点走过就跳过
			if(d[v]>d[u]+s[u][j].second)//如果发现有结点联系他们并且可以缩短距离就更行距离
			{
				d[v]=d[u]+s[u][j].second;
				p.push(make_pair(d[v]*(-1),v));//将与u相关的点压入队列中
				color[v]=1;
			}
		}
	}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值