[最短路] aw1129. 热浪(单源最短路建图+spfa循环队列+模板题)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[最短路] aw1129. 热浪(单源最短路建图+spfa循环队列+模板题)相关的知识,希望对你有一定的参考价值。
1. 题目来源
链接:1129. 热浪
相关链接:
2. 题目解析
显然,本题是单源最短路问题,边权为正,数据量不大,dijkstra
和 堆优化 dijkstra
、spfa
均可。其中 bellman_ford
算法在单源最短路问题中,用的非常少,几乎不用。
由于 spfa
需要使用到队列,且每个点可能入队多次,普通队列长度固定,可能会造成数组下标越界。所以,需要使用循环队列来进行优化。
静态数组模拟循环队列注意点:
- 初始化
hh=0, tt=1
,队列判空:hh==tt
。 hh
含义依旧和普通队列一样,指向队头元素,出队依旧采用q[hh ++ ]
,后置++
。tt
含义与普通队列稍有不同,指向队尾元素的下一个空位置。即当hh==tt
时,队列就为空了,入队时,这个空位置需要使用,则q[tt ++]
,也是后置++
。这个与普通队列使用q[ ++ tt]
有所不同。实际上在本题,使用前置++
也是可以过的。- 循环队列需要注意一点,当
hh==N
或者tt==N
时,说明这两个指针都已经越界了,由于它是循环队列,需要将其指向开头,即hh=0
,tt=0
。这也是循环队列的一般写法。 - 由于,
spfa
算法中有st
数组的存在,会判断队列中点是否存在,如果存在,则不会重复入队,故队列中点至多有效会存在n
个,所有使用循环队列当hh=0
时,不会影响还在队列中未出队的点。
以后 spfa
还是尽量使用循环队列写法吧。
时间复杂度: O ( 相 应 最 短 路 算 法 的 时 间 复 杂 度 ) O(相应最短路算法的时间复杂度) O(相应最短路算法的时间复杂度)
空间复杂度: O ( 同 上 ) O(同上) O(同上)
spfa
的循环队列写法:
#include <bits/stdc++.h>
using namespace std;
const int N = 2505, M = 6205 * 2;
int n, m, S, T;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
int q[N]; // 在此为循环队列
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void spfa() {
memset(dist, 0x3f, sizeof dist);
int hh = 0, tt = 1;
q[0] = S, dist[S] = 0, st[S] = true;
while (hh != tt) {
auto t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if (!st[j]) {
q[tt ++ ] = j;
if (tt == N) tt = 0;
st[j] = true;
}
}
}
}
}
int main() {
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- ) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
spfa();
printf("%d\\n", dist[T]);
return 0;
}
三种最短路算法在本题的应用:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 2505, M = 6205 * 2;
int n, m, ts, te;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra() {
memset(dist, 0x3f, sizeof dist);
dist[ts] = 0; // 设置起点为 ts
for (int i = 0; i < n; i ++ ) {
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
st[t] = true;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i])
dist[j] = dist[t] + w[i];
}
}
return dist[te];
}
int dijkstra_heap() {
memset(dist, 0x3f, sizeof dist);
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, ts});
dist[ts] = 0;
while (heap.size()) {
auto t = heap.top(); heap.pop();
int d = t.first, idx = t.second;
if (st[idx]) continue;
st[idx] = true;
for (int i = h[idx]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > d + w[i]) {
dist[j] = d + w[i];
heap.push({dist[j], j});
}
}
}
return dist[te];
}
int spfa() {
memset(dist, 0x3f, sizeof dist);
queue<int> q;
q.push(ts);
dist[ts] = 0;
st[ts] = true;
while (q.size()) {
auto t = q.front(); q.pop();
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return dist[te];
}
int main() {
scanf("%d%d%d%d", &n, &m, &ts, &te);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ ) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
// int res = dijkstra();
// int res = spfa();
int res = dijkstra_heap();
printf("%d\\n", res);
return 0;
}
以上是关于[最短路] aw1129. 热浪(单源最短路建图+spfa循环队列+模板题)的主要内容,如果未能解决你的问题,请参考以下文章
[最短路] aw903. 昂贵的聘礼(单源最短路建图+超级源点+知识理解+好题)
[最短路] aw1127. 香甜的黄油(单源最短路建图+模板题)