欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)

程序员文章站 2024-02-29 12:35:58
...

题目背景

本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入格式

第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。

接下来 m 行每行包含三个整数 u,v,w,表示一条 uvu \to v 的,长度为 w 的边。

输出格式

输出一行 n 个整数,第 i 个表示 s 到第 i 个点的最短路径,若不能到达则输出 23112^{31}-1

输入输出样例

输入 #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

说明/提示

【数据范围】

对于 20%20\% 的数据:1n51\le n \le 51m151\le m \le 15
对于 40%40\% 的数据:1n1001\le n \le 1001m1041\le m \le 10^4
对于 70%70\% 的数据:1n10001\le n \le 10001m1051\le m \le 10^5
对于 100%100\% 的数据:1n1041 \le n \le 10^41m5×1051\le m \le 5\times 10^5,保证数据随机。

对于真正 100%100\% 的数据,请移步 P4779。请注意,该题与本题数据范围略有不同。

样例说明:

图片1到3和1到4的文字位置调换

思路

模拟过程(摘自SPFA 算法详解(最短路径)有删改)
P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)
首先建立起始点a到其余各点的最短路径表格

a b c d e f g
d[i] 0 inf inf inf inf inf inf

首先源点a入队,当队列非空时:

    队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

a b c d e f g
d[i] 0 24 8 15 inf inf inf

在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点需要入队,此时,队列中新入队了三个结点b,c,d

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

a b c d e f g
d[i] 0 24 8 15 30 inf inf

在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要入队,此时队列中的元素为c,d,e

队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

a b c d e f g
d[i] 0 24 8 15 15 11 inf

在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此e不用入队了,f要入队,此时队列中的元素为d,e,f

队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

a b c d e f g
d[i] 0 24 8 15 15 11 19

队首元素e点出队,对以d为起始点的所有边的终点依次进行松弛操作,在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

a b c d e f g
d[i] 0 24 8 15 13 11 14

在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

a b c d e f g
d[i] 0 17 8 15 13 11 14

在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

a b c d e f g
d[i] 0 17 8 15 13 11 14

在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

a b c d e f g
d[i] 0 17 8 15 13 11 14

在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

最终a到g的最短路径为14

a b c d e f g
d[i] 0 17 8 15 13 11 14

Code

图的存储采用链式向前星,不懂请点击<这里>

(看完模拟过程和链式向前星的你可以自己写出来么,建议先自己上手试试,代码很简单。)

#include<bits/stdc++.h>//SPFA算法
using namespace std;
#define INF (1<<31)-1
class node
{
public:
	int to, next, w;
}edge[500010];
int n, m, s, u, v, w, cnt = 0, head[10010],  vis[10010];
long long dis[10010];
queue<int> a;
void add(int u, int v, int w)//链式向前星
{
	edge[++cnt].w = w;
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void SPFA()
{
	for (int i = 1; i <= n; i++)
		dis[i] = INF;
	a.push(s);
	vis[a.front()] = 1;
	dis[s] = 0;//源点到自身的距离为零
	while (!a.empty())
	{
		for (int i = head[a.front()]; i; i = edge[i].next)//对相连的点进行松弛操作
			if (dis[edge[i].to] > dis[a.front()] + edge[i].w)//更新距离
			{
				dis[edge[i].to] = dis[a.front()] + edge[i].w;
				if (!vis[edge[i].to])//队列中无此结点
				{
					a.push(edge[i].to);
					vis[edge[i].to] = 1;
				}
			}
		vis[a.front()] = 0;
		a.pop();
	}
}
int main()
{
	cin >> n >> m >> s;
	for (int i = 1; i <= m; i++)//存图
	{
		cin >> u >> v >> w;
		add(u, v, w);
	}
	SPFA();
	for (int i = 1; i <= n; i++)
		cout << dis[i]<<" ";
	return 0;
}
如果你看懂了SPFA,那么最后这张图送给你:

P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)

死因:毒瘤数据。

所以在没有负环(某个重复进入队列的节点)的情况下不建议使用SPFA,请放心随机数据的话肯定会有,出题人要是想卡SPFA那就请换Dijkstra再来!

相关标签: 洛谷刷题