[最短路] aw1129. 热浪(单源最短路建图+spfa循环队列+模板题)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[最短路] aw1129. 热浪(单源最短路建图+spfa循环队列+模板题)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:1129. 热浪

相关链接:

2. 题目解析

显然,本题是单源最短路问题,边权为正,数据量不大,dijkstra 和 堆优化 dijkstraspfa 均可。其中 bellman_ford 算法在单源最短路问题中,用的非常少,几乎不用。

由于 spfa 需要使用到队列,且每个点可能入队多次,普通队列长度固定,可能会造成数组下标越界。所以,需要使用循环队列来进行优化。


静态数组模拟循环队列注意点:

  • 初始化 hh=0, tt=1,队列判空:hh==tt
  • hh 含义依旧和普通队列一样,指向队头元素,出队依旧采用 q[hh ++ ],后置 ++
  • tt 含义与普通队列稍有不同,指向队尾元素的下一个空位置。即当 hh==tt 时,队列就为空了,入队时,这个空位置需要使用,则 q[tt ++],也是后置 ++。这个与普通队列使用 q[ ++ tt] 有所不同。实际上在本题,使用前置 ++ 也是可以过的。
  • 循环队列需要注意一点,当 hh==N 或者 tt==N 时,说明这两个指针都已经越界了,由于它是循环队列,需要将其指向开头,即 hh=0tt=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. 昂贵的聘礼(单源最短路建图+超级源点+知识理解+好题)

1129. 热浪单源最短路板子题

第三章 图论未完成

[最短路] aw1127. 香甜的黄油(单源最短路建图+模板题)

[最短路] aw1128. 信使(单源最短路建图+Floyd算法+最短路理解+模板题)

[最短路] aw1126. 最小花费(单源最短路建图+知识理解+代码细节+好题)