题目背景

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

题目描述

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

输入格式

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

接下来 m 行每行包含三个整数 u,v,w,表示一条 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径

输出格式

输出一行 n 个整数,第 i 个表示 s 到第 i 个点的最短路径,若不能到达则输出 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_数据_02

输入输出样例

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

说明/提示

【数据范围】

对于 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_出队_03 的数据:P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_数据_04P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_数据_05
对于 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_出队_06 的数据:P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_出队_07P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_出队_08
对于 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_数据_09 的数据:P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径_10P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_出队_11
对于 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径_12 的数据:P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径_13P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径_14,保证数据随机。

对于真正 P3371 【模板】单源最短路径(弱化版)(C++_SPFA算法_链式向前星)_最短路径_12 的数据,请移步 P4779。请注意,该题与本题数据范围略有不同。

样例说明:

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

思路

模拟过程(摘自SPFA 算法详解(最短路径)有删改)

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


首先建立起始点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算法_链式向前星)_最短路径_17

死因:毒瘤数据。

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