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

【代码超详解】洛谷 P3371 P4779 单源最短路径(Dijkstra 算法 · 模板题)

程序员文章站 2022-05-20 20:02:52
...

一、题目描述

P3371 【模板】单源最短路径(弱化版)

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

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

输入格式
第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式
一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

输入输出样例
输入 #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
说明/提示
时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=15;

对于40%的数据:N<=100,M<=10000;

对于70%的数据:N<=1000,M<=100000;

对于100%的数据:N<=10000,M<=500000。保证数据随机。

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

样例说明:
【代码超详解】洛谷 P3371 P4779 单源最短路径(Dijkstra 算法 · 模板题)
图片1到3和1到4的文字位置调换

P4779 【模板】单源最短路径(标准版)

【代码超详解】洛谷 P3371 P4779 单源最短路径(Dijkstra 算法 · 模板题)

二、算法分析说明与代码编写指导

Dijkstra算法
1、用途:求连通图的定点到其余点的最短距离。
2、条件限制:要求不存在负边。

3、算法内容: 
设带权连通图G(V, E),边权代表边长(直接相连两点的通路长度),定点(源点)s,数组d[|V|]代表定点s到各点的通路的估计最短距离。
记d[s] = 0,d的其余元素记为一个较大值M,设集合U代表已经估计最短距离的顶点集合,
也可以用一个bitset来标记顶点是否已被估计过最短距离。
【1】首先找出定点s的邻接点中通路距离最短的点x,并记d[x] = |sx|,将定点s先加入集合U。
【2】对点x,对该点的任意邻接点y,更新d[y] = min(d[y], d[x] + weight(x, y)) = min(|sy|, |sx| + |xy|)。
更新完毕后,将x加入集合U。这一步也叫做“松弛”。
【3】对未加入集合U的其它顶点,选出d值最小的顶点x’,更新其任意邻接点y’:
d[y’] = min(d[y’], d[x’] + weight(x’, y’)) = min(|sy’|, |sx’| + |x’y’|),
将x’放入集合U。直到U = V(|U| = |V|)。此时d[|V|]代表定点s到各点的通路的最短距离。
容易发现,步骤【1】【2】是需要重复执行的步骤【3】的特殊情况,选出来的点x恰好是s的邻接点,且通路最短。
由于每次将新的顶点加入集合U之后,都要查找下一个未估计最短距离的顶点,因此总的时间复杂度按O(|V|2)估计。
证明:/

4、算法优化:
下面通过priority_queue将复杂度降低至O(|E| log |V|)。
设带权连通图G(V, E),边权代表边长(直接相连两点的通路长度),定点(源点)s,数组d[|V|]代表定点s到各点的通路的估计最短距离。
用一个edge结构体表示邻接关系和边(至少含有终点和权两个成员),并额外标记顶点是否已被估计d值。
设优先队列q,用于保存顶点和对应的d值(可以构造一个有序对来存储距离和对应的顶点)。记d[s] = 0,d的其余元素记为一个较大值M。
【1】将定点s压入优先队列q。
【2】循环:取出q的队首x,如果已经被估计过,则continue(跳到循环开头继续)。
否则,对队首的顶点的全部邻接点,更新d[y] = min(d[y], d[x] + weight(x, y)) = min(|sy|, |sx| + |xy|),
并将该邻接点压入优先队列q(为了在全部顶点中取得d最小的顶点)。队列空时,循环结束。
证明:/
一个具有|V|个顶点的图G(V, E)最多可以含有|V|(|V|1)|V|2条边(无重边时)。
而|V|较大时,O(|E| log |V|)O(|V|2)的复杂度高,此时不应该进行堆优化。
对这样的稠密图,直接采用邻接矩阵存图,套用O(|V|2)的Dijkstra算法即可。

5、补充说明
特殊情形:当所有边的边权相等,可以直接采用BFS求得最短路。
当图不是连通图时,选定的定点无法到达的点的距离保持M未更新。
当图存在负边时,算法会给出错误结果。

三、AC 代码

P3371:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<bitset>
#include<map>
#pragma warning(disable:4996)
using namespace std;
const unsigned M = 2147483647;
unsigned n, m, s, u, x, d[10001];
struct edge { unsigned to, dis; };
vector<edge> G[10001]; bitset<10001> visited; edge e; pair<unsigned, unsigned> p;
priority_queue<pair<unsigned, unsigned>, vector<pair<unsigned, unsigned>>, greater<pair<unsigned, unsigned>>> q;
int main() {
	scanf("%u%u%u", &n, &m, &s);
	for (unsigned i = 0; i < m; ++i) {
		scanf("%u%u%u", &u, &e.to, &e.dis);
		G[u].emplace_back(e);
	}
	fill(d + 1, d + n + 1, M); d[s] = 0; p.first = d[s]; visited.reset(); p.second = s; q.emplace(p);
	while (q.empty() == false) {
		x = q.top().second; q.pop();
		if (visited[x])continue;
		visited[x] = true;
		for (unsigned long long y = 0; y < G[x].size(); ++y) {
			e = G[x][y];
			if (d[e.to] > d[x] + e.dis) {
				d[e.to] = d[x] + e.dis; p.first = d[e.to]; p.second = e.to; q.emplace(p);
			}
		}
	}
	for (unsigned i = 1; i <= n; ++i)printf("%u ", d[i]);
	//system("pause");
	return 0;
}

P4779:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<bitset>
#include<map>
#pragma warning(disable:4996)
using namespace std;
const unsigned M = 2147483647;
unsigned n, m, s, u, x, d[100001];
struct edge { unsigned to, dis; };
vector<edge> G[100001]; bitset<100001> visited; edge e; pair<unsigned, unsigned> p;
priority_queue<pair<unsigned, unsigned>, vector<pair<unsigned, unsigned>>, greater<pair<unsigned, unsigned>>> q;
int main() {
	scanf("%u%u%u", &n, &m, &s);
	for (unsigned i = 0; i < m; ++i) {
		scanf("%u%u%u", &u, &e.to, &e.dis);
		G[u].emplace_back(e);
	}
	fill(d + 1, d + n + 1, M); d[s] = 0; p.first = d[s]; visited.reset(); p.second = s; q.emplace(p);
	while (q.empty() == false) {
		x = q.top().second; q.pop();
		if (visited[x])continue;
		visited[x] = true;
		for (unsigned long long y = 0; y < G[x].size(); ++y) {
			e = G[x][y];
			if (d[e.to] > d[x] + e.dis) {
				d[e.to] = d[x] + e.dis; p.first = d[e.to]; p.second = e.to; q.emplace(p);
			}
		}
	}
	for (unsigned i = 1; i <= n; ++i)printf("%u ", d[i]);
	//system("pause");
	return 0;
}
相关标签: 详解