超详细单源最短路详解(part1. Dijkstra)

求点赞!!!谢谢!!!(后面有空会去b站录个讲解)(哦还有要是哪里写的不对大家麻烦帮我指出来谢谢!!!)

基本概念

单源最短路问题:给出一张个结点,m条边的图G(有向图或无向图),第i条边代价为a[i],求给定结点s到所有节点的最短距离

比如说:

 

 在这张图中,假如将dis[i]记录为S点到点Vi的最短路距离,则dis数组应当为:{0,14,29,33,24,21}

现在我们理解了什么叫做单源最短路,那么用什么算法解决呢?

DIJKSTRA 登场!~

***使用要求:不能有负权(想处理负权请期待下期spfa)

暴力办法:

-dis[u]表示当前源点s到u的最短距离。

-我们维护两个点集:点集A中的点u,dis值为最终值,不会更优了,点集B则还不保证最优。

Step 1:初始化:dis[s]=0,其他设为inf值0x3f3f3f3f,点集B中存储所有的点.

Step 2:取出B中dis值最小的点u,vis[u]设为1,遍历u所有的连边u-v,若dis[v]>dis[u]+w,那么更新dis[v].

//这个是伪代码
memset(dis,INF,sizeof(dis));
dis[s]=0//初始化
vis[s]=1;//点s进A集合
while(B集合不为空)
{
    1.找出dis最小,且在B集合中的点u
    2.vis[u]=1//点u进A集合
    3. 枚举u->(v,w)
        4. 更新
}
//最后答案就在dis数组里啦~

OK,这个暴力解我们就掌握了!可以推算出,复杂度为O(n^2)

好暴力啊!!!咋办呢?做优化啊!!!

正解

我们看到,其实我们的解里面很暴力的一个地方就是找出dis最小这个地方,那么我们可以用一些特殊的数据结构进行优化,比如priority_queue,O(logn)的时间代替O(n)的查找时间,最后我们的正解的复杂度是O((n+m)logm)

最后咱们搬运洛谷的题目来讲解一下吧~~~

题目描述

给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。

数据保证你能从 s 出发到任意点。

输入格式

第一行为三个正整数 n, m, s。 第二行起 m 行,每行三个非负整数 u_i, v_i, w_i,表示从 u_i​ 到 v_i​ 有一条权值为 w_i​ 的有向边。

输出格式

输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。

输入输出样例

输入 #1

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出 #1

0 2 4 3

上代码!(今天新学的快读函数lollll

#include<bits/stdc++.h>
#define pii pair<long long,int>

using namespace std;

int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

struct tNode{
	int u,v,w,nxt;
}g_e[200010];
int head[200010],cnt=0;
int n,m,s;
long long dis[200010];
bool vis[200010];
priority_queue<pii,vector<pii>,greater<pii> > Q;

void add_edge(int u,int v,int w)//链式前向星存图
{
	g_e[++cnt]=(tNode){u,v,w,head[u]};
	head[u]=cnt;
}

void dij()
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[s]=0;//初始化
	Q.push(make_pair(dis[s],s));
	while(!Q.empty())
	{
		pii tmp=Q.top();//pair排序比较方便所以这么写的
		Q.pop();
		int u=tmp.second;
		long long d=tmp.first;
		if(vis[u]) continue;//加入A集合
		vis[u]=1;
		for(int i=head[u];i;i=g_e[i].nxt)//遍历连边
		{
			int v=g_e[i].v,w=g_e[i].w;
			if(dis[v]>dis[u]+w)//更新
			{
				dis[v]=dis[u]+w;
				if(!vis[v])
				{
					Q.push(make_pair(dis[v],v));
				}
			}
		}
	}
} 

int main()
{
	n=read(),m=read(),s=read();
	int u,v,w;
	for(int i=1;i<=m;++i)
	{
		u=read(),v=read(),w=read();
		add_edge(u,v,w);
	}
	dij();
	for(int i=1;i<=n;++i)
	{ 
		printf("%lld ",dis[i]);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值