参考文章

文章目录

  • 一、单源最短路
    • 1. Dijkstra(堆优化)
      • 模板
      • 例题
      • Dijkstra 线段树优化 // 手抄了一遍还是没懂,等我强了再来
    • 2. SPFA
  • 二、全源最短路
    • Floyd
  • 三、k短路
  • 四、判环(SPFA)
    • 判正环
    • 判负环

一、单源最短路

1. Dijkstra(堆优化)

DJ 的思想是每次用一个点更新与其相邻的所有点,更新后将该点标记(每个点只用一次),从用过的点连通分量出发,找一条连通分量与外界的连边,用该边的终点继续更新与其相连点到起点的距离。 O(n^2)
因为复杂度主要由边决定,适合稀疏图
不能处理负边权
利用堆优化,取出最短的一条O(nlogn),用其终点继续向外更新。

自定义比较操作符

模板

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int> PII; // first 存距离   second 存编号
const int maxn = 2e5+10;
int n, m;
int cnt = 0;
int dist[maxn];
bool st[maxn]; // 第 i 个点的最短路是否确定,是否需要更新
int head[maxn];
struct Edge {int to, next, w;
}e[maxn];void add(int u, int v, int w) {e[++cnt].next = head[u];e[cnt].to = v;e[cnt].w = w;head[u] = cnt;
}void init() {memset(dist, 0x3f3f3f3f, sizeof(dist)); // 将所有距离初始化为正无穷 memset(head, 0, sizeof(head));memset(e, 0, sizeof(e));cnt = 0;
}int dijkstra() {dist[1] = 0; // 第一个点到起点的距离priority_queue<PII, vector<PII>, greater<PII>> heap; // 小根堆 heap.push({0,1});while(heap.size()) { // 堆不空 PII t = heap.top();heap.pop();int ver = t.second, dis = t.first;if(st[ver]) continue; // 重边(访问过的)就不用再更新了,DJ思想就是贪心的访问每条最短边st[ver] =  true; // 标记 t 已经确定为最短路for(int i = head[ver]; i; i = e[i].next) {int to = e[i].to;if(dist[to] > dis + e[i].w) {dist[to] = dis + e[i].w;heap.push({dist[to], to});}} }
} int main() {//  freopen("test.in", "r", stdin);while(~scanf("%d%d", &n, &m)) {init();for(int i = 1; i <= m; i++) {int u, v, w;scanf("%d%d%d", &u, &v, &w);add(u, v, w);}dijkstra();int to; // 目标点 scanf("%d", &to);if(dist[to] != 0x3f3f3f3f) {printf("%d\n", dist[n]); // 到达目标的最短距离。 DJ计算一个起点,多个终点 }else printf("无法到达!\n");}return 0;
}

例题

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

RE了好几次,总结一下RE原因

  1. 数组越界
  2. 站溢出
  3. 除0
  4. 函数格式(void int 是否有返回值)
    AC代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
// RE:数组越界,站溢出,除0 , 函数格式(void int 是否有返回值)
using namespace std;
typedef pair<int,int> PII; // first 存距离   second 存编号
const int maxn = 5e5+10;
int n, m;
int cnt = 0;
int dist[10005];
bool st[maxn]; // 第 i 个点的最短路是否确定,是否需要更新
int head[maxn];
struct Edge {int to, next, w;
}e[maxn];void add(int u, int v, int w) {e[++cnt].next = head[u];e[cnt].to = v;e[cnt].w = w;head[u] = cnt;
}void init() {for(int i = 0; i <= n; i++) dist[i] = (1<<31)-1; // 将所有距离初始化为正无穷 memset(head, 0, sizeof(head));memset(e, 0, sizeof(e));cnt = 0;
}void dijkstra(int s) {dist[s] = 0; // 第一个点到起点的距离priority_queue<PII, vector<PII>, greater<PII>> heap; // 小根堆 heap.push({0,s});while(heap.size()) { // 堆不空 PII t = heap.top();heap.pop();int ver = t.second, dis = t.first;if(st[ver]) continue; // 重边(访问过的)就不用再更新了,DJ思想就是贪心的访问每条最短边st[ver] =  true; // 标记 t 已经确定为最短路for(int i = head[ver]; i; i = e[i].next) {int to = e[i].to;if(dist[to] > dis + e[i].w) {dist[to] = dis + e[i].w;heap.push({dist[to], to});}} }
} int main() {//  freopen("test.in", "r", stdin);int s;scanf("%d%d%d", &n, &m, &s);init();for(int i = 1; i <= m; i++) {int u, v, w;scanf("%d%d%d", &u, &v, &w);add(u, v, w);}dijkstra(s);for(int i = 1; i <= n; i++) {printf("%d", dist[i]); // 到达目标的最短距离。 DJ计算一个起点,多个终点 if(i == n)  break;else printf(" ");}return 0;
}

Dijkstra 线段树优化 // 手抄了一遍还是没懂,等我强了再来

留坑

2. SPFA

先说一下Bellman-Ford 算法:
每次取一条边加入连通块,然后从该边的终点作为中转点更新整个图的最短路,最坏情况就是每次加入一条边都能更新整个图的最短路, 即 V*E
复杂度 O(VE)

SPFA(优化Bellman-Ford)是把一个点周围所有点放进队列来更新最短值,优化就在有导向性,只用更新与松弛成功的点相连的点

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int head[maxn], cnt = 0, vis[maxn];
int dis[maxn];struct Edge{int to, w, next;
}e[maxn];void add(int u, int v, int w) {e[++cnt].next = head[u];e[cnt].to = v;e[cnt].w = w;head[u] = cnt;
}void spfa(int s)  {memset(dis, 0x3f3f3f3f, sizeof(dis));dis[s] = 0; vis[s] = 1; queue<int> Q;Q.push(s);int u, v;while(!Q.empty()) {u = Q.front(); Q.pop(); vis[u] = 0;for(int i = head[u]; i; i = e[i].next) {v = e[i].to;if(dis[u] + e[i].w < dis[v]) {dis[v] = dis[u] + e[i].w;if(!vis[v]) Q.push(v), vis[v] = 1; // 不在队列里就放进去更新别的点 }}}
}void init() {memset(head, 0, sizeof(head));memset(vis, 0, sizeof(vis));memset(e, 0, sizeof(e));cnt = 0;
}int main() {//  freopen("test.in", "r", stdin);int n, m, s;while(scanf("%d%d%d", &n, &m, &s) == 3) {init();int u, v, w;for(int i = 1; i <= m; i++) scanf("%d%d%d", &u, &v, &w), add(u, v, w); // 有向图连一次边就好spfa(s);for(int i = 1; i <= n; i++) printf("%d ", dis[i]);}return 0;
}

二、全源最短路

Floyd

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;int dis[maxn][maxn];void Floyd(int n) {for(int k = 1; k <= n; k++)for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
}void init() {memset(dis, 0x3f, sizeof(dis));
}int main() {freopen("test.in", "r", stdin);int n, m, s;while(~scanf("%d%d%d", &n, &m, &s)){init();int u, v, w;for(int i = 1; i <= m; i++) scanf("%d%d%d", &u, &v, &w), dis[u][v] = w;Floyd(n);for(int i = 1; i <= n; i++) printf("%d ", dis[s][i]);}return 0;
}

三、k短路

留坑

四、判环(SPFA)

用 spfa 判断正负环都可以利用某个点被松弛 n 次以上作为依据

判正环

判断正环的例题
E - Currency Exchange POJ - 1860

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
int N, M, S;
double V;
struct edge {int to;double R, C;
};
vector<edge> G[105];int vis[105], cnt[105];double d[105];bool spfa() {queue<int> Q;Q.push(S);vis[S] = 1;d[S] = V;while(Q.size()) {int u = Q.front(); Q.pop();vis[u] = 0; // 出队for(int i = 0; i < G[u].size(); i++) {edge e = G[u][i];double val = (d[u] - e.C) * e.R;if(val - d[e.to] > 1e-8) {d[e.to] = val;if(vis[e.to]) continue;Q.push(e.to);   // 每次更新完一个点,要把它周围的点加入队列继续往外更新其他点 vis[e.to] = 1;if(++cnt[e.to] > N) return 1;// 如果一个点入队 N 次以上,说明出现环了 }}}return 0;
} int main() {//  freopen("test.in", "r", stdin);ios::sync_with_stdio(false); // 打消iostream中输入输出缓存,节省时间。cin.tie(0); cout.tie(0); // 可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。cin >> N >> M >> S >> V;int a, b;double Rab, Cab, Rba, Cba;for(int i = 0; i < 105; i++) G[i].clear();for(int i = 0; i < M; i++) {cin >> a >> b >> Rab >> Cab >> Rba >> Cba;edge e;e.to = b, e.R = Rab, e.C = Cab;G[a].push_back(e);e.to = a, e.R = Rba, e.C = Cba;G[b].push_back(e);} if(spfa()) { // 正环使钱变多 cout << "YES" << endl;}else cout << "NO" << endl;return 0;
}

判负环

利用 SPFA 由于每个点记录的是从起点出发到达当前点的最短路,只有当一个点松弛成功它才能作为一个中转点去松弛其他点,所以如果一个点如果被松弛 n 次,那么存在负环,否则一个点至多被松弛 n-1 次

负环例题
F - Wormholes POJ - 3259

只要出现一个负环,则沿这个负环不停走,一定能使时间不断前走,走一定次数以后一定能满足提前返回出发点

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
const int inf = 1e9+7;
using namespace std;
const int maxn = 3e3+10;
int N, M, K;
int num[maxn]; // 记录每个点出现的次数
int dis[600];bool vis[600];
struct node {int to, w;
};vector<node> G[maxn];void init() {memset(vis, 0, sizeof(vis));memset(num, 0, sizeof(num)); memset(dis, inf, sizeof(dis));
}bool spfa(int s) {queue<int> Q;Q.push(s); vis[s] = 1;dis[s] = 0; num[s] = 1;while(!Q.empty()) {int u = Q.front(); Q.pop(); vis[u] = 0;for(int i = 0; i < G[u].size(); i++) {node e = G[u][i];if(dis[u] < inf && dis[e.to] > (dis[u] + e.w)) {dis[e.to] = dis[u] + e.w; // 在队列中只是不重复添加,但是需要更新值 if(vis[e.to]) continue;Q.push(e.to); vis[e.to] = 1;if(++num[e.to] > N) return 1; // 出现负环 }}}return 0;
}int main() {//  freopen("test.in", "r", stdin);ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int F; cin >> F;while(F--) {cin >> N >> M >> K;init();for(int i = 1; i <= N; i++) G[i].clear(); int u, v, t;node e;for(int i = 1; i <= M; i++) {cin >> u >> v >> t;e.to = v; e.w = t;G[u].push_back(e);e.to = u; e.w = t;G[v].push_back(e);}for(int i = 1; i <= K; i++) {cin >> u >> v >> t;e.to = v; e.w = -t;G[u].push_back(e);}if(spfa(1)) cout << "YES" << endl;else cout << "NO" << endl;}return 0;
}

最短路专题(不完整)相关推荐

  1. Kuangbin最短路专题

    菜鸡第一篇博客 整理一下kuangbin的最短路专题(主要是ide出问题了没事干 目录 一.POJ 2387  Till the Cows Home SPFA板子 二.POJ 2253 Frogger ...

  2. 图论01.最短路专题_学习笔记+模板

    图论01.最短路专题_学习笔记+模板 一.定义与性质 ● 需要的前导知识点 路径 最短路 有向图中的最短路.无向图中的最短路 单源最短路.每对结点之间的最短路 ● 最短路的性质 对于边权为正的图,任意 ...

  3. ZZNUOJ_C语言1103:平均学分绩点(函数专题)(完整代码)

    题目描述 平均学分绩点(Grade Point Average,即GPA)是以学分与绩点作为衡量学生学习的量与质的计算单位,以取得一定的学分和平均学分绩点作为毕业和获得学位的标准,实施多样的教育规格和 ...

  4. 天梯赛最短路专题 -- 旅游规划、城市间紧急救援

    最短路 7-2 旅游规划 (25分) 7-4 城市间紧急救援 (25分) 7-2 旅游规划 (25分) https://pintia.cn/problem-sets/13053317694120222 ...

  5. ZZNUOJ_C语言1112:进制转换(函数专题)(完整代码)

    题目描述 输入一个十进制整数n,输出对应的二进制整数.常用的转换方法为"除2取余,倒序排列".将一个十进制数除以2,得到余数和商,将得到的商再除以2,依次类推,直到商等于0为止,倒 ...

  6. POJ - 1847 Tram(最短路)

    题目链接:点击查看 题目大意:火车从起点开到终点,轨道上有很多岔路口,每个岔路口都有很多方向(火车能够选择任意一个方向行驶),但是默认 的是第一个方向,如果要选择其他的方向需要增加一次切换的操作,问最 ...

  7. 第K短路(A*(astar)算法)

    给定一张N个点(编号1,2-N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边. 注意: 每条最短路中至少要包含一条边. 输入格式 第一行包含两个整数N和M. 接下来M行 ...

  8. 蒟蒻成长之路(持续更新)

    蒟蒻成长之路 (这个玩意只是闲着写写, 写给自己看的) 开始 开始日期:2023年3月23日20:55:24 内容 主要记录一些做题日常和快乐的学校生活 初一:2022~2023 Day1--2023 ...

  9. linux 运行msi文件是什么意思,查看Msi文件内容

    Baseadapter与Simpleadapter之争 作者:andyrat,联系方式:andyrat@qq.com c#字符串操作方法实例 # 字符串是使用 string 关键字声明的一个字符数组. ...

最新文章

  1. java收银台程序gui_javaSE小项目---简易收银台
  2. Prometheus — 软件架构
  3. linux新手入门必看
  4. python函数参数的作用是_python函数参数理解
  5. C3P0 释放连接 的问题
  6. 专家 安全_船级社专家组到公司开展安全生产标准化考核
  7. python计算precision,recall,f1-score
  8. Mysql高性能优化技能总结
  9. 使用Arrays sort 方法進行排序
  10. 在大公司天天调参数,感觉快废了
  11. smarty内建函数
  12. UVA11428 Cubes【数学+二分】
  13. nginx反向代理转发apache配置 之 cookie去哪儿了?
  14. linux 截图软件安装,Ubuntu 安装截图工具Shutter,并设置快捷键 Ctrl+Alt+A
  15. vue笔记 keyboard+隐藏input光标+自动跳转到下一个input
  16. python批量处理excel——给指定单元格填充颜色
  17. ES6新特性:解构、对象扩展、函数扩展、数组扩展、数值扩展
  18. 个人所得税法应充分体现经济法原则
  19. python+matplotlib绘制南丁格尔玫瑰图
  20. 微信小程序页面跳转,url传参参数丢失问题

热门文章

  1. 12个现实世界中的机器学习真相
  2. 【ZZ】浅谈大型web系统架构 | 菜鸟教程
  3. git for c#, clone方法
  4. Elasticsearch增删改查
  5. String Reduction问题分析
  6. 【一次面试】再谈javascript中的继承
  7. 夏季编辑系统的联想感觉
  8. VS2005中删除最近打开的项目和文件的记录
  9. .NET基础示例系列之六:委托及事件
  10. 都有Python了,还要什么编译器