图的存储与最短路问题
一、图的储存
1.邻接矩阵
- 邻接矩阵使用二维 v e c t o r vector vector 存储数据
- d [ i ] [ j ] d[i][j] d[i][j] 表示从点 i i i 到点 j j j 的权值 有向图存储一个即可,无向图还需要存储 d [ j ] [ i ] d[j][i] d[j][i]
优点:
1.表示方式直接,直观易于理解
2.在稀疏图上节省空间
缺点:
1.二维 vector 在访问时会比较耗时
总结:邻接矩阵所以只适合处理 ∣ N ∣ < = 2500 |N|<= 2500 ∣N∣<=2500 的问题
2.链式前向星储存图
- 以一个结构体将需要存储的图的四个参数存储起来
struct Edge{
int u, v, w, next;
}edge[N];
u u u(起点), v v v(终点), w w w(权值), n e x t next next(上一个点的序号)
- 以 h e a d head head存储最后一个 u u u 的序号, 同时head初始化为-1。
——图片仅供参考
这里 t o to to 相当于 v v v , f r o m from from 相当于 u u u
- 链式前向星添加点的方式为:
void add_edge(int u,int v,int w){ edge[e].u=u; edge[e].v=v; edge[e].w=w; edge[e].next=head[u]; head[u]=e++; }
- 链式向前星的遍历方式为 :
for (int u = 1; u <= n; u++) { cout << "u= " << u << '\n'; for (int i = head[u]; i != -1; i = nxt[i]) { cout << "v= " << pnt[i] << ", w= " << wv[i] << '\n'; } }
优点:
1.省时间, 占用空间小
总结:链式前向星是目前最主流的储存图的方法, 推荐大家使用
——图的储存完
二、最短路算法
1. S P F A _ H E A P ( D i j k s t r a ( 迪杰斯特拉算法 ) + h e a p ( 堆优化 ) ) 1.SPFA\_HEAP(Dijkstra(迪杰斯特拉算法) + heap(堆优化)) 1.SPFA_HEAP(Dijkstra(迪杰斯特拉算法)+heap(堆优化))
——在这里我就直接向大家介绍堆优化过的迪杰斯特拉算法的最简写法,也是最好用的最短路算法
/*
SPFA 算法是 Bellman-Ford算法 的队列优化算法的称,
通常用于求含负权边的单源最短路径,以及判负权环
*/
//------------- 堆优化 dijkstra最短路 O(nlog(m))
vector<int> dj(m + 1, 200005), pd(n + 1, 0);
dj[s] = 0;
//把起点的值设为0,表示起点自己到自己的最短路为0
priority_queue<pair<int, int>> q;
//用pair<int, int> 第一位是权值,第二位是u起点
q.push({dj[s], s});
while (!q.empty()) {
//当q里面还有数时,表明还没有把起点到任意点的距离算完
int k = 0;
int w = q.top().first, u = q.top().second;
w = -w;
//因为我们插入时将w变成了-w,所以这里要反一下
q.pop();
//更新完就弹出,表示已经更新过了
if (dj[u] != w) continue;
// dp[p] != -w 表示 p 已经访问过
for (int j = head[u]; j != -1; j = edge[j].next) {
int v = edge[j].v, z = dj[u] + edge[j].w;
if (dj[v] > z) q.push({-(dj[v] = z), v});
// 因为优先队列每次弹出的都是最大的数
//而我们希望每次从最小的数开始更新
//所以插入时需要取反
}
}
cout << dj[t];
/*------------- 堆优化 dijkstra最短路 O(nlog(m)) end ------*/
2. F l o y d ( 弗洛伊德算法 ) 2.Floyd(弗洛伊德算法) 2.Floyd(弗洛伊德算法)
迪杰斯特拉算法适合求固定两点的最短路,但如果要求一个图里面任意两点的最短路,推荐使用 F l o y d Floyd Floyd 算法
题目 :
-
给定一个 n 个点 m 条边的 有向图,图中可能存在重边和自环,边权可能为负数。
-
再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,输出 i m p o s s i b l e impossible impossible。
代码如下:
#include <bits/stdc++.h> using namespace std; const int INF = 1000000000; int main() { int n, m, t; cin >> n >> m >> t; vector<vector<int>> dj(n + 1, vector<int> (n + 1, INF)); // dj[u][v] 表示从 u 到 v 的 最短距离 for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; dj[u][v] = min(dj[u][v], w); // 重边取较小 } for (int i = 1; i <= n; i++) dj[i][i] = 0; // 自己到自己的距离为 0 for (int k = 1; k <= n; k++) { for (int u = 1; u <= n; u++) { for (int v = 1; v <= n; v++) { if (u == k || u == v || k == v) continue; dj[u][v] = min(dj[u][v], dj[u][k] + dj[k][v]); // 经不经过第k个点 } } } const int PD = 200000005; while (t--) { int u, v; cin >> u >> v; if (dj[u][v] < PD) cout << dj[u][v] << '\n'; //是否为负环 else cout << "impossible" << '\n'; } return 0; }
三、具体题目
1.非负权单源最短路
题目:给一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1≤n≤10^5) n(1≤n≤105) 个点 m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1≤m≤2⋅10 ^ 5) m(1≤m≤2⋅105)条边的 无向图,所有边权都为正,求 s s s 到 t t t 的最短路。
这题直接套模板就好了
——代码如下 :
#include <bits/stdc++.h>
#define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next)
using namespace std;
const int M = 200005, N = 100005;
int e = 0;
vector<int> head(N, -1);
struct Edge {
int u, v, w, next;
} edge[M];
void add_edge(int u, int v, int w) {
edge[e].u = u;
edge[e].v = v;
edge[e].w = w;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n, m, s, t;
cin >> n >> m >> s >> t;
for (int i = 0; i < m; i++) {
int u1, v1, w1;
cin >> u1 >> v1 >> w1;
add_edge(u1, v1, w1);
add_edge(v1, u1, w1);
}
vector<int> dj(m + 1, 200005), pd(n + 1, 0);
dj[s] = 0;
priority_queue<pair<int, int>> q;
q.push({dj[s], s});
while (!q.empty()) {
int k = 0;
int w = q.top().first, u = q.top().second;
// auto [w,u]=q.top();
w = -w;
q.pop();
if (dj[u] != w) {
continue;
}
for (int j = head[u]; j != -1; j = edge[j].next) {
int v = edge[j].v, z = dj[u] + edge[j].w;
if (dj[v] > z) q.push({-(dj[v] = z), v});
}
}
cout << dj[t];
return 0;
}
2.带负权单源最短路
题目:给一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1≤n≤10^5) n(1≤n≤105) 个点 m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1≤m≤2⋅10 ^5) m(1≤m≤2⋅105)条边的有向图,边权都有正有负,求 s s s 到 t t t 的最短路,保证 s s s 到 t t t 之间至少存在一条路。
- 如果 s s s 到 t t t 的路径可以无穷小,输出 − I N F - INF −INF
专门用一个数组存从 s s s 到 v v v 经过的点的数量,如果经过的点的数量大于 n n n 就证明有负环
——代码如下 :
#include <bits/stdc++.h> #define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next) using namespace std; const int M = 200005, N = 100005; int e = 0; vector<int> head(N, -1); struct Edge { int u, v, w, next; } edge[M]; void add_edge(int u, int v, int w) { edge[e].u = u; edge[e].v = v; edge[e].w = w; edge[e].next = head[u]; head[u] = e++; } int main() { int Min = 0; int n, m, s, t; cin >> n >> m >> s >> t; for (int i = 0; i < m; i++) { int u1, v1, w1; cin >> u1 >> v1 >> w1; add_edge(u1, v1, w1); } vector<int> dj(m + 1, 200005), pd(n + 1, 0); dj[s] = 0, pd[s] = 1; priority_queue<pair<int, int>> q; q.push({dj[s], s}); while (!q.empty()) { int k = 0, w = q.top().first, u = q.top().second; w = -w; if (pd[u] > n) { cout << "-INF"; return 0; } q.pop(); if (dj[u] != w) continue; for (int j = head[u]; j != -1; j = >edge[j].next) { int v = edge[j].v, z = dj[u] + edge[j].w; if (dj[v] > z) q.push({-(dj[v] = z), v}), >pd[v] = pd[u] + 1; } pd[u]++; } cout << dj[t]; return 0; }
3.P9751 [CSP-J 2023] 旅游巴士
https://www.luogu.com.cn/problem/P9751
题目描述
小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。
旅游景点的地图共有 n n n 处地点,在这些地点之间连有 m m m 条道路。其中 1 1 1 号地点为景区入口, n n n 号地点为景区出口。我们把一天当中景区开门营业的时间记为 0 0 0 时刻,则从 0 0 0 时刻起,每间隔 k k k 单位时间便有一辆旅游巴士到达景区入口,同时有一辆旅游巴士从景区出口驶离景区。
所有道路均只能单向通行。对于每条道路,游客步行通过的用时均为恰好 1 1 1 单位时间。
小 Z 希望乘坐旅游巴士到达景区入口,并沿着自己选择的任意路径走到景区出口,再乘坐旅游巴士离开,这意味着他到达和离开景区的时间都必须是 k k k 的非负整数倍。由于节假日客流众多,小 Z 在旅游巴士离开景区前只想一直沿着景区道路移动,而不想在任何地点(包括景区入口和出口)或者道路上停留。
出发前,小 Z 忽然得知:景区采取了限制客流的方法,对于每条道路均设置了一个
“开放时间”
a
i
a _ i
ai,游客只有不早于
a
i
a _ i
ai 时刻才能通过这条道路。
请帮助小 Z 设计一个旅游方案,使得他乘坐旅游巴士离开景区的时间尽量地早。
输入格式
输入的第一行包含 3 个正整数 n , m , k n, m, k n,m,k,表示旅游景点的地点数、道路数,以及旅游巴士的发车间隔。
输入的接下来 m m m 行,每行包含 3 个非负整数 u i , v i , a i u _ i, v _ i, a_ i ui,vi,ai,表示第 i i i 条道路从地点 u i u _ i ui 出发,到达地点 v i v _ i vi,道路的“开放时间”为 a i a _ i ai。
输出格式
输出一行,仅包含一个整数,表示小 Z 最早乘坐旅游巴士离开景区的时刻。如果不存在符合要求的旅游方案,输出 -1
。
思路:
-
- d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 u u u到 i i i余数为 j j j的最短路
——代码如下:
#include <bits/stdc++.h>
using namespace std;
int e = 0;
const int M = 20005, N = 10005;
vector<int> head(N, -1);
struct Edge {
int u, v, w, next;
} edge[M];
void add_edge(int u, int v, int w) {
edge[e].u = u;
edge[e].v = v;
edge[e].w = w;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
}
vector<vector<int>> dp(n + 1, vector<int>(k, -1));
priority_queue<tuple<int, int, int>> q;
dp[1][0] = 0;
q.push({0, 1, 0});
while (!q.empty()) {
auto [w, u, xj] = q.top();
q.pop();
w = -w;
if (w != dp[u][xj]) continue;
for (int j = head[u]; j != -1; j = edge[j].next) {
int w1 = w + 1, w2 = w + (edge[j].w - w + k - 1) / k * k + 1, v = edge[j].v;
int xk = w1 % k, yk = w2 % k;
if (w >= edge[j].w) {
if (dp[v][xk] == -1 or dp[v][xk] > w1) q.push({-(dp[v][xk] = w1), v, xk});
} else if (dp[v][yk] == -1 or dp[v][yk] > w2) q.push({-(dp[v][yk] = w2), v, yk});
}
}
cout << dp[n][0];
return 0;
}
// int w2 = w + (edge[j].w - w + k - 1) / k * k + 1
4.有边数限制的最短路
https://www.acwing.com/problem/content/855/
题目:
- 给定一个 n n n 个点 m m m 条边的 有向图,图中可能存在重边和自环, 边权可能为负数。
- 请你求出从 1 号点到 n n n 号点的 最多经过 k 条边 的最短距离,如果无法从 1 号点走到 n n n 号点,输出 i m p o s s i b l e 。 impossible。 impossible。
注意:图中可能 存在负权回路 。
——代码:
#include <bits/stdc++.h>
using namespace std;
const int M = 10005, INF = 1000000000;
struct Edge {
int x, y, z, next;
} edge[M];
int main() {
int n, m, k;
cin >> n >> m >> k;
vector<int> head(n + 1, -1);
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
edge[i].x = x;
edge[i].y = y;
edge[i].z = z;
edge[i].next = head[x];
head[x] = i;
}
vector<vector<int>> dp(n + 1, vector<int>(k + 1, INF));
priority_queue<tuple<int, int, int>> q;
dp[1][0] = 0;
q.push({-dp[1][0], 1, 0});
for (int i = 1; i <= k; i++) {
for (int u = 1; u <= n; u++) {
for (int j = head[u]; j != -1; j = edge[j].next) {
int v1 = edge[j].y, temp = edge[j].z + dp[u][i - 1];
if (temp < dp[v1][i]) dp[v1][i] = temp;
}
}
}
int minsum = INF;
for (int i = k; i >= 1; i--) {
minsum = min(minsum, dp[n][i]);
}
if (minsum <= 5000000) {
cout << minsum << '\n';
return 0;
}
cout << "impossible" << '\n';
return 0;
}
最多更新 k k k次,所以可以不用队列优化,直接枚举即可。
5.求任意2点间最短路
题目:
- 给定一个 n 个点 m 条边的 有向图,图中可能存在重边和自环,边权可能为负数。
- 再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 i m p o s s i b l e impossible impossible。
这题用 F l o y d Floyd Floyd 即可, 代码如下:
#include <bits/stdc++.h>
using namespace std;
const int INF = 1000000000;
int main() {
int n, m, t;
cin >> n >> m >> t;
vector<vector<int>> dj(n + 1, vector<int> (n + 1, INF));
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
dj[u][v] = min(dj[u][v], w);
}
for (int i = 1; i <= n; i++) {
dj[i][i] = 0;
}
for (int k = 1; k <= n; k++) {
for (int u = 1; u <= n; u++) {
for (int v = 1; v <= n; v++) {
if (u == k || u == v || k == v) continue;
dj[u][v] = min(dj[u][v], dj[u][k] + dj[k][v]);
}
}
}
const int PD = 200000005;
while (t--) {
int u, v;
cin >> u >> v;
if (dj[u][v] < PD) cout << dj[u][v] << '\n';
else cout << "impossible" << '\n';
}
return 0;
}
6.聚会
原题链接:https://www.acwing.com/problem/content/5478/
题目:
-
约翰的农场有 n n n 个谷仓,编号 1 ∼ n 1∼n 1∼n,谷仓之间有 m m m 条双向道路。
-
所有道路的长度均为 1。
-
奶牛们可以通过这些道路从任意谷仓到达任意其它谷仓。
-
任意两个谷仓之间最多只包含一条道路(注意是道路,不是路径)。
-
不存在道路两端都连接同一个谷仓的情况。
-
农场中一共有 k k k 种干草,编号 1 ∼ k 1∼k 1∼k。
-
每个谷仓都存有一种干草,其中第 i i i 个谷仓存有第 a i a_i ai种干草。
-
每种干草都至少存在于一个谷仓中。
-
奶牛们计划选择一个谷仓举办干草宴会。为了让宴会足够丰盛,至少需要将 s s s 种不同的干草汇集在宴会谷仓。
-
宴会谷仓本身就包含一种干草,而其它干草就需要从其它谷仓运输。
已知,将一种干草从一个谷仓运至另一个谷仓所需的运输成本等于两谷仓之间的最短路径距离。
请你帮助奶牛们计算,对于每个谷仓,如果挑选其为宴会举办地,则举办宴会需要付出的总运输成本的最小可能值是多少。
思路:
dp[i][k] 表示从点
i
i
i到第
k
k
k种粮食的最短路
——代码如下 :
#include <bits/stdc++.h>
#define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next)
using namespace std;
const int M = 200005, N = 100005;
int e = 0;
vector<int> head(N, -1);
struct Edge {
int u, v, w, next;
} edge[M];
void add_edge(int u, int v, int w) {
edge[e].u = u;
edge[e].v = v;
edge[e].w = w;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n, m, k, s; //n 个谷仓 , m 条路 , k种干草 ,需要 s 种干草
cin >> n >> m >> k >> s;
vector<int> a1(n + 1, 0);// 第 i 个稻谷存的种类
vector<vector<int>> c(k + 1);
for (int i = 1; i <= n; i++) {
cin >> a1[i];
c[a1[i]].push_back(i);// 第 i 种稻谷存在了哪几个谷仓里
}
for (int i = 0; i < m; i++) {
int u, v, w = 1;
cin >> u >> v;
add_edge(u, v, 1);
add_edge(v, u, 1);
// 运输成本为1, 因为为无向图所以输入两次边
}
vector<vector<int>> dp(n + 1, vector<int>(k + 1, -1));
// dp[i][k] 表示以 i 为起点到 第 k 种粮食的最短路
for (int i = 1; i <= k; i++) {
int ans = 0;
priority_queue<tuple<int, int, int>> q;
for (int k = 0; k < c[i].size(); k++) {
q.push({-(dp[c[i][k]][i] = 0), c[i][k], i});
// 起点到自己的距离为0
}
while (!q.empty()) {
auto [w, u, k2] = q.top();
q.pop();
w = -w;
if (w != dp[u][k2]) continue;
for (int j = head[u]; j != -1; j = edge[j].next) {
int temp = w + 1;
int v1 = edge[j].v;
if (dp[v1][k2] == -1 || dp[v1][k2] > temp) q.push({-(dp[v1][k2] = temp), v1, k2});
}
}
//spfa算法
}
for (int i = 1; i <= n; i++) {
sort(dp[i].begin() + 1, dp[i].end());
int ans = 0;
for (int j = 1; j <= s; j++) {
ans += dp[i][j];
}
cout << ans << " ";
//计算前s条路的最短路和
}
return 0;
}
7.最小边权最大的路径
原题链接:https://vijos.org/p/1391
题目:
- 小衫困在某个监狱,监狱是由 n n n 个小房间和 m m m 条小房间之间的单向的管道组成的。
- 对于某个管道,小杉只能在人品不超过一定程度时通过。
- 小杉一开始在房间 1,现在小杉想知道,每个小房间他最多能够以人品多少的状态到达。
注意,小杉的人品在出发以后是不会改变的。
思路:
只需要在
S
P
F
A
SPFA
SPFA的基础上改变 , 把用作比较的“
t
e
m
p
temp
temp”改为“
t
e
m
p
=
m
i
n
(
r
,
e
d
g
e
[
i
]
.
r
)
temp = min(r, edge[i].r)
temp=min(r,edge[i].r)” 就可以了
——代码如下 :
#include <bits/stdc++.h>
#define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next)
using namespace std;
const int M = 200005, N = 100005;
int e = 0;
vector<int> head(N, -1);
struct Edge {
int u, v, r, next;
} edge[M];
void add_edge(int u, int v, int r) {
edge[e].u = u;
edge[e].v = v;
edge[e].r = r;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v, r;
cin >> u >> v >> r;
add_edge(u, v, r);
}
vector<int> dp(n + 1, -1);
priority_queue<pair<int, int>> q;
dp[1] = 1000000005;
q.push({dp[1], 1});
while (!q.empty()) {
auto [r, u] = q.top();
q.pop();
if (r != dp[u]) continue;
for (int i = head[u]; i != -1; i = edge[i].next) {
int u1 = edge[i].u, v = edge[i].v, r1 = edge[i].r;
int temp = min(r, edge[i].r);
if (dp[v] == -1 || temp > dp[v]) q.push({dp[v] = temp, v});
}
}
for (int i = 2; i <= n; i++) {
cout << dp[i] << '\n';
}
return 0;
}
8.最小费用
题目 :
- 给定平面上的 n 个点,定义点 ( x 1 , y 1 ) (x_1 , y_1) (x1,y1) 到点 ( x 2 , y 2 ) (x_2 , y_2) (x2,y2) 的费用为 m i n ( ∣ x 1 − x 2 ∣ , ∣ y 1 − y 2 ∣ ) min(|x_1 - x_2| , |y_1 - y_2|) min(∣x1−x2∣,∣y1−y2∣) 。
求从 1 号点走到 n 号点的最小费用。
思路:
这题并不需要任意两个点之间都建边,只需要排序后按 x x x 坐标和 y y y 坐标建就可以了,因为第 x x x 坐标 i i i 小的点的离他最近的点一定是 x x x 坐标第 ( i − 1 ) (i - 1) (i−1) 小的点。
——代码如下 :
#include <bits/stdc++.h>
#define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next)
using namespace std;
const int M = 200005, N = 100005;
int e = 0;
vector<int> head(N, -1);
struct Edge {
int u, v, w, next;
} edge[M];
void add_edge(int u, int v, int w) {
edge[e].u = u;
edge[e].v = v;
edge[e].w = w;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n;
cin >> n;
vector<int> xa(n + 1, 0), ya(n + 1, 0);
vector<pair<int, int>> x(n + 1, {0, 0}), y(n + 1, {0, 0});
for (int i = 1; i <= n; i++) {
cin >> x[i].first >> y[i].first;
x[i].second = i;
y[i].second = i;
xa[i] = x[i].first;
ya[i] = y[i].first;
}
sort(x.begin() + 1, x.end());
sort(y.begin() + 1, y.end());
for (int i = 1; i < n; i++) {
int xa1 = -xa[x[i].second] + xa[x[i + 1].second];
add_edge(x[i].second, x[i + 1].second, xa1);
add_edge(x[i + 1].second, x[i].second, xa1);
}
for (int i = 1; i < n; i++) {
int ya1 = -ya[y[i].second] + ya[y[i + 1].second];
add_edge(y[i].second, y[i + 1].second, ya1);
add_edge(y[i + 1].second, y[i].second, ya1);
}
vector<int> dp(n + 1, -1);
priority_queue<pair<int, int>> q;
dp[1] = 0;
q.push({dp[1], 1});
while (!q.empty()) {
auto [w, u] = q.top();
q.pop();
w = -w;
if (w != dp[u]) continue;
for (int j = head[u]; j != -1; j = edge[j].next) {
int v = edge[j].v, w1 = edge[j].w;
int temp = w + w1;
if (dp[v] == -1 || temp < dp[v]) q.push({-(dp[v] = temp), v});
}
}
cout << dp[n] << '\n';
return 0;
}
9.飞行路线
原题网址:https://www.luogu.com.cn/problem/P4568
题目 :
-
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n n n 个城市设有业务,设这些城市分别标记为 0 0 0 到 n − 1 n-1 n−1,一共有 m m m 种航线,每种航线连接两个城市,并且航线有一定的价格。
-
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k k k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?
思路:
- dp[i][j] 表示从点s出发到点i的还剩j次免费转乘机会的最短路
——代码如下 :
#include <bits/stdc++.h>
#define repe(j, k) for(int j=head[k];j!=-1;j=edge[j].next)
using namespace std;
const int M = 200005, N = 100005;
int e = 0;
vector<int> head(N, -1);
struct Edge {
int u, v, w, next;
} edge[M];
void add_edge(int u, int v, int w) {
edge[e].u = u;
edge[e].v = v;
edge[e].w = w;
edge[e].next = head[u];
head[u] = e++;
}
int main() {
int n, m, k;
cin >> n >> m >> k;
// 分别表示城市数,航线数和免费乘坐次数。
int s, t;
cin >> s >> t;
//从城市s出发到城市t
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
add_edge(v, u, w);
//无向图
}
vector<vector<int>> dp(n + 1, vector<int>(k + 1, -1));
priority_queue<tuple<int, int, int>> q;
dp[s][k] = 0;
// dp[i][j] 表示从点s出发到点i的还剩j次免费转乘机会的最短路
q.push({dp[s][k], s, k});
while (!q.empty()) {
auto [w, u, k1] = q.top();
q.pop();
w = -w;
if (w != dp[u][k1]) continue;
for (int j = head[u]; j != -1; j = edge[j].next) {
int v = edge[j].v, w1 = edge[j].w;
int temp = w + w1, temp1 = w;
if (dp[v][k1] == -1 || temp < dp[v][k1] ) {
q.push({-(dp[v][k1] = temp), v, k1});
}
if ((k1 - 1) >= 0 && (dp[v][k1 - 1] == -1 || temp1 < dp[v][k1 - 1]) ) {
q.push({-(dp[v][k1 - 1] = temp1), v, k1 - 1});
}
}
}
//SPFA
sort(dp[t].begin(), dp[t].end());
cout << dp[t][0];
return 0;
}
——图的存储与最短路算法完
标签:存储,dj,int,短路,head,next,算法,edge,dp From: https://blog.csdn.net/weixin_54990292/article/details/141474448