最短路专题(不完整)
参考文章
文章目录
- 一、单源最短路
- 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原因
- 数组越界
- 站溢出
- 除0
- 函数格式(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;
}
最短路专题(不完整)相关推荐
- Kuangbin最短路专题
菜鸡第一篇博客 整理一下kuangbin的最短路专题(主要是ide出问题了没事干 目录 一.POJ 2387 Till the Cows Home SPFA板子 二.POJ 2253 Frogger ...
- 图论01.最短路专题_学习笔记+模板
图论01.最短路专题_学习笔记+模板 一.定义与性质 ● 需要的前导知识点 路径 最短路 有向图中的最短路.无向图中的最短路 单源最短路.每对结点之间的最短路 ● 最短路的性质 对于边权为正的图,任意 ...
- ZZNUOJ_C语言1103:平均学分绩点(函数专题)(完整代码)
题目描述 平均学分绩点(Grade Point Average,即GPA)是以学分与绩点作为衡量学生学习的量与质的计算单位,以取得一定的学分和平均学分绩点作为毕业和获得学位的标准,实施多样的教育规格和 ...
- 天梯赛最短路专题 -- 旅游规划、城市间紧急救援
最短路 7-2 旅游规划 (25分) 7-4 城市间紧急救援 (25分) 7-2 旅游规划 (25分) https://pintia.cn/problem-sets/13053317694120222 ...
- ZZNUOJ_C语言1112:进制转换(函数专题)(完整代码)
题目描述 输入一个十进制整数n,输出对应的二进制整数.常用的转换方法为"除2取余,倒序排列".将一个十进制数除以2,得到余数和商,将得到的商再除以2,依次类推,直到商等于0为止,倒 ...
- POJ - 1847 Tram(最短路)
题目链接:点击查看 题目大意:火车从起点开到终点,轨道上有很多岔路口,每个岔路口都有很多方向(火车能够选择任意一个方向行驶),但是默认 的是第一个方向,如果要选择其他的方向需要增加一次切换的操作,问最 ...
- 第K短路(A*(astar)算法)
给定一张N个点(编号1,2-N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边. 注意: 每条最短路中至少要包含一条边. 输入格式 第一行包含两个整数N和M. 接下来M行 ...
- 蒟蒻成长之路(持续更新)
蒟蒻成长之路 (这个玩意只是闲着写写, 写给自己看的) 开始 开始日期:2023年3月23日20:55:24 内容 主要记录一些做题日常和快乐的学校生活 初一:2022~2023 Day1--2023 ...
- linux 运行msi文件是什么意思,查看Msi文件内容
Baseadapter与Simpleadapter之争 作者:andyrat,联系方式:andyrat@qq.com c#字符串操作方法实例 # 字符串是使用 string 关键字声明的一个字符数组. ...
最新文章
- java收银台程序gui_javaSE小项目---简易收银台
- Prometheus — 软件架构
- linux新手入门必看
- python函数参数的作用是_python函数参数理解
- C3P0 释放连接 的问题
- 专家 安全_船级社专家组到公司开展安全生产标准化考核
- python计算precision,recall,f1-score
- Mysql高性能优化技能总结
- 使用Arrays sort 方法進行排序
- 在大公司天天调参数,感觉快废了
- smarty内建函数
- UVA11428 Cubes【数学+二分】
- nginx反向代理转发apache配置 之 cookie去哪儿了?
- linux 截图软件安装,Ubuntu 安装截图工具Shutter,并设置快捷键 Ctrl+Alt+A
- vue笔记 keyboard+隐藏input光标+自动跳转到下一个input
- python批量处理excel——给指定单元格填充颜色
- ES6新特性:解构、对象扩展、函数扩展、数组扩展、数值扩展
- 个人所得税法应充分体现经济法原则
- python+matplotlib绘制南丁格尔玫瑰图
- 微信小程序页面跳转,url传参参数丢失问题