单源最短路径

问题描述

分别求出从起点到其他所有点的最短路径,这次主要介绍两种算法,Dijkstra和SPFA。若无负权优先Dijkstra算法,存在负权选择SPFA算法。

Dijkstra算法

  • 非负权
  • 稳定

Dijkstra算法的解决方案

Dijkstra提出按各顶点与源点v间的路径长度的递增次序,生成到各顶点的最短路径的算法。既先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从源点v 到其它各顶点的最短路径全部求出为止。

Dijkstra算法的解题思想

将图G中所有的顶点V分成两个顶点集合S和T。以v为源点已经确定了最短路径的终点并入S集合中,S初始时只含顶点v,T则是尚未确定到源点v最短路径的顶点集合。然后每次从T集合中选择S集合点中到T路径最短的那个点,并加入到集合S中,并把这个点从集合T删除。直到T集合为空为止。

具体步骤

  1. 选一顶点v为源点,并视从源点v出发的所有边为到各顶点的最短路径(确定数据结构:因为求的是最短路径,所以①就要用一个记录从源点v到其它各顶点的路径长度数组dist[],开始时,dist是源点v到顶点i的直接边长度,即dist中记录的是邻接阵的第v行。②设一个用来记录从源点到其它顶点的路径数组path[],path中存放路径上第i个顶点的前驱顶点)。

  2. 在上述的最短路径dist[]中选一条最短的,并将其终点(即<v,k>)k加入到集合s中。

  3. 调整T中各顶点到源点v的最短路径。 因为当顶点k加入到集合s中后,源点v到T中剩余的其它顶点j就又增加了经过顶点k到达j的路径,这条路径可能要比源点v到j原来的最短的还要短。调整方法是比较dist[k]+g[k,j]与dist[j],取其中的较小者。

  4. 再选出一个到源点v路径长度最小的顶点k,从T中删去后加入S中,再回去到第三步,如此重复,直到集合S中的包含图G的所有顶点。


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

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

$100 \rightarrow 60 $;

\(\text{Ag} \rightarrow \text{Cu}\) ;

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

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

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

输入格式

第一行为三个正整数 \(n, m, s\) 。 第二行起 mm 行,每行三个非负整数 \(u_i\), \(v_i\), \(w_i\) ,表示从 u_iu**i 到 v_iv**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

说明/提示

样例解释请参考 数据随机的模板题

\(1 \leq n \leq 10^5\) ;

\(1 \leq m \leq 2\times 10^5\) ;

\(s = 1\) ;

\(1 \leq u_i, v_i\leq n\) ;

\(0 \leq w_i \leq 10 ^ 9\) ,

\(0 \leq \sum w_i \leq 10 ^ 9\) 。

本题数据可能会持续更新,但不会重测,望周知。

2018.09.04 数据更新 from @zzq


// 预定义
#include <cstdio>
#include <queue>
#define MAXN 100000
#define MAXM 200000
#define IFN ((1<<31)-1)
int cnt, n, m, s, head[MAXN + 1], dis[MAXN + 1];
// 用vis[]记录是否已经找到最短路径
bool vis[MAXN + 1];
// 构造边
struct Edge {
int to, dis, next;
}edge[MAXM + 1];
// 构造节点
struct node {
// 当前节点以及当前节点的最短路径
int pos, dis;
// 重载运算符,使dis小的优先级更高
inline bool operator < (const node& a)const {
return dis > a.dis;
}
};
// 创建有限队列
std::priority_queue<node> heap;
// 快读
inline int read() {
int num = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { num = (num<<1) + (num<<3) + (ch^48);ch = getchar(); }
return f * num;
}
// 链式向前星添加边
inline void add(int from, int to, int dis) {
edge[++cnt].dis = dis;
edge[cnt].to = to;
edge[cnt].next = head[from];
head[from] = cnt;
}
// Dijkstra求最短路径
void Dijkstra() {
while (!heap.empty()) {
// 获取当前距离起点最短的节点
int cpos = heap.top().pos; heap.pop();
// 若这个节点已经遍历过所有的边则pass
if (vis[cpos]) continue;
vis[cpos] = 1;
// 遍历当前节点的所有边
for (int i = head[cpos]; i; i = edge[i].next) {
int to = edge[i].to;
// 若发现到达下一个点的更短路径更新dis
if (dis[to] > dis[cpos] + edge[i].dis) {
dis[to] = dis[cpos] + edge[i].dis;
// 队列中实时更新已经找到的路径
heap.push(node({ to,dis[to] }));
}
}
}
}
int main() {
n = read(), m = read(), s = read();
// 预处理
for (int i = 0; i < MAXN + 1; i++) {
dis[i] = IFN;
}
dis[s] = 0;
heap.push(node({ s,0 }));
// 存图
for (int i = 1, u, v, w; i <= m; i++) {
u = read(), v = read(), w = read();
add(u, v, w);
}
// 求最短路径
Dijkstra();
// 输出
for (int i = 1; i <= n; i++)
printf("%d ", dis[i]);
return 0;
}

SPFA算法

描述

SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,在Bellman-ford算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。在 NOI2018Day1T1归程 中正式被卡,其时间复杂度为O(nm),远劣于Dijkstra的O((n+m)log m)。

SPFA算法的解题思想

我们约定加权有向图G不存在负权回路,即最短路径一定存在。如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。


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

题目背景

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

题目描述

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

输入格式

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

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

输出格式

输出一行 \(n\) 个整数,第 \(i\) 个表示 \(s\) 到第 \(i\) 个点的最短路径,若不能到达则输出 \(2^{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\%\) 的数据:\(1\le n \le 5\) ,\(1\le m \le 15\) ;

对于 \(40\%\) 的数据:\(1\le n \le 100\) ,\(1\le m \le 10^4\) ;

对于 \(70\%\) 的数据:\(1\le n \le 1000\) ,\(1\le m \le 10^5\) ;

对于 \(100\%\) 的数据:\(1 \le n \le 10^4\) ,\(1\le m \le 5\times 10^5\) ,保证数据随机。

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

样例说明:

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

完整解答

#include <cstdio>
#include <queue>
#define MAXN 10000
#define MAXM 500000
#define IFN (1<<31)-1
int cnt, n, m, s, head[MAXN+1], dis[MAXN+1], vis[MAXN+1];
struct Edge {
int to, w, next;
}edge[MAXM+1];
// 快读
inline int read() {
int num=0, f = 1;char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-')f = -1;ch = getchar(); }
while (ch >= '0' && ch <= '9') { num = (num << 3) + (num << 1) + (ch ^ 48);ch = getchar(); }
return num * f;
}
inline void add(int from, int to, int w) {
edge[++cnt].w = w;
edge[cnt].to = to;
edge[cnt].next = head[from];
head[from] = cnt;
}
std::queue<int> q;
void SPFA() {
// 开始节点进队列
q.push(s);
while (!q.empty()) {
// 获取当前节点,出队列时取消标记
int cpos = q.front(); q.pop();vis[cpos] = 0;
for (int i = head[cpos]; i; i = edge[i].next) {
int to = edge[i].to;
// 若可优化才进队列
if (dis[to] > dis[cpos] + edge[i].w) {
dis[to] = dis[cpos] + edge[i].w;
// 进队列加标记
if (!vis[to]) {q.push(to);vis[to] = 1;}
}
// 不可优化直接出队,当队列为空时无可优化节点
}
}
}
int main() {
n = read(), m = read(), s = read();
for (int i = 0; i < MAXN + 1; i++) {
dis[i] = IFN;
}
dis[s] = 0;
for (int i = 1,u,v,w; i <= m; i++) {
u = read(), v = read(), w = read();
add(u, v, w);
}
SPFA();
for (int i = 1; i <= n; i++) {
printf("%d ", dis[i]);
}
return 0;
}

[数据结构与算法-15]单源最短路径(Dijkstra+SPFA)的更多相关文章

  1. 单源最短路径 dijkstra算法实现

    本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图.而且连通,有向图,不连通图的做法相似. 算法简述: 首先确定"单源"的源.假设是第0个顶点. 维护三个数组 ...

  2. 单源最短路径Dijkstra算法,多源最短路径Floyd算法

    1.单源最短路径 (1)无权图的单源最短路径 /*无权单源最短路径*/ void UnWeighted(LGraph Graph, Vertex S) { std::queue<Vertex&g ...

  3. Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例

    本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...

  4. 【算法】单源最短路径和任意两点最短路径总结(补增:SPFA)

    [Bellman-Ford算法] [算法]Bellman-Ford算法(单源最短路径问题)(判断负圈) 结构: #define MAX_V 10000 #define MAX_E 50000 int ...

  5. 51nod 1445 变色DNA ( Bellman-Ford算法求单源最短路径)

    1445 变色DNA 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 有一只特别的狼,它在每个夜晚会进行变色,研究发现它可以变成N种颜色之一,将这些颜色标号为0,1 ...

  6. Til the Cows Come Home(poj 2387 Dijkstra算法(单源最短路径))

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 32824   Accepted: 11098 Description Bes ...

  7. Dijkstra算法解决单源最短路径

    单源最短路径问题:给定一个带权有向图 G = (V, E), 其中每条边的权是一个实数.另外,还给定 V 中的一个顶点,称为源.现在要计算从源到其他所有各顶点的最短路径长度.这里的长度是指路上各边权之 ...

  8. 单源最短路径——dijkstra算法

    dijkstra算法与prim算法的区别   1.先说说prim算法的思想: 众所周知,prim算法是一个最小生成树算法,它运用的是贪心原理(在这里不再证明),设置两个点集合,一个集合为要求的生成树的 ...

  9. 【转】Dijkstra算法(单源最短路径)

    原文:http://www.cnblogs.com/dolphin0520/archive/2011/08/26/2155202.html 单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路 ...

随机推荐

  1. Codeforces Beta Round #92 (Div. 2 Only) B. Permutations

    You are given n k-digit integers. You have to rearrange the digits in the integers so that the diffe ...

  2. AtCoder Beginner Contest 173 D - Chat in a Circle (贪心)

    题意:有一个空环和\(n\)个点,每次可以选择一个点放在空环上,并且获得周围两个点中最小的那个的权值,问能获得的最大的权值是多少? 题解:我们每次都优先放最大的进去,注意每次放的时候都要将这个点放在当 ...

  3. Centos7 搭建Nginx+rtmp+hls直播推流服务器

    1 准备工具 使用yum安装git [root~]# yum -y install git 下载nginx-rtmp-module,官方github地址 // 通过git clone 的方式下载到服务 ...

  4. 说说Golang goroutine并发那些事儿

    摘要:今天我们一起盘点一下Golang并发那些事儿. Golang.Golang.Golang 真的够浪,今天我们一起盘点一下Golang并发那些事儿,准确来说是goroutine,关于多线程并发,咱 ...

  5. Kibana 地标图可视化

    ElasticSearch 可以使用 ingest-geoip 插件可以在 Kibana 上对 IP 进行地理位置分析, 这个插件需要 Maxmind 的 GeoLite2 City,GeoLite2 ...

  6. Redis Cluster 分布式集群(下)

    Redis Cluster 搭建(工具) 环境准备 节点 IP 端口 节点① 172.16.1.121 6379,6380 节点② 172.16.1.122 6379,6380 节点③ 172.16. ...

  7. JavaScript DOM操作之查找元素节点

    概要: 编程接口 可通过 JavaScript 对 HTML DOM 进行访问. 所有 HTML 元素被定义为对象,而编程接口则是对象方法和对象属性. 方法是您能够执行的动作(比如添加或修改元素). ...

  8. JavaScript基本包装类介绍

    为了便于操作基本类型值,ECMAScript 提供了 3 个特殊的引用类型:Boolean.Number和 String.这些类型与其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为.实际上 ...

  9. popstate 事件 & history API

    popstate 事件 & history API URL change 当用户浏览会话历史记录时,活动历史记录条目发生更改时,将触发 Window 界面的 popstate 事件. 它将当前 ...

  10. ES进行date_histogram时间聚合,聚合结果时间不正确问题

    在做项目中,有一个需求是统计本周内每天的漏洞数量,我选用的是ES中的date_histogram函数来进行聚合统计: 但是出现了一个问题,聚合出来的结果和想要统计的结果时间不一致,如下图所示 时间区间 ...