题解:洛谷 P1339 Heat Wave

【题目来源】

洛谷:P1339 [USACO09OCT] Heat Wave G - 洛谷

【题目描述】

有一个 \(n\) 个点 \(m\) 条边的无向图,请求出从 \(s\)\(t\) 的最短路长度。

【输入】

第一行四个正整数 \(n,m,s,t\)
接下来 \(m\) 行,每行三个正整数 \(u,v,w\),表示一条连接 \(u,v\),长为 \(w\) 的边。

【输出】

输出一行一个整数,表示答案。

【输入样例】

7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1

【输出样例】

7

【核心思想】

  1. 问题分析:给定 \(n\) 个点、\(m\) 条边的无向带权图,求从 \(s\)\(t\) 的最短路径长度。这是一个单源最短路问题,边权为正整数,适合使用 Dijkstra 算法

  2. 算法选择

    • Dijkstra(朴素版):适用于稠密图(邻接矩阵存储),每次 \(O(n)\) 找最小距离点,总复杂度 \(O(n^2)\)
    • 反向求解技巧:代码中调用 dij(t) 从终点 \(t\) 出发,输出 \(d[s]\),等价于从 \(s\)\(t\) 的最短路(无向图对称性)
  3. 关键步骤

    • 初始化:邻接矩阵 \(e\) 初始化为 \(\infty\)0x3f),读入 \(n, m, s, t\)\(m\) 条无向边
    • Dijkstra 过程(从源点 \(s\) 出发):
      • 初始化距离数组 \(d[i] = \infty\)\(d[s] = 0\)
      • 循环 \(n-1\) 次:
        • 找未访问且距离最小的点 \(u\)\(u = \arg\min_{j \notin vis} d[j]\)
        • 标记 \(vis[u] = 1\)
        • 松弛所有点:\(d[j] = \min(d[j],\ d[u] + e[u][j])\)
    • 输出\(d[t]\)(代码中为 \(d[s]\),因从 \(t\) 反向计算)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\),每次找最小点 \(O(n)\),松弛 \(O(n)\),共 \(n-1\)
    • 空间复杂度:\(O(n^2)\),邻接矩阵存储
  5. Dijkstra 的核心思想

    • 贪心选择:每次选择当前距离源点最近的未确定点,其最短距离已确定(正权边保证)
    • 松弛操作:利用已确定最短距离的点更新其邻接点的距离上界
    • 无向图对称性\(d(s, t) = d(t, s)\),可从任一端点运行 Dijkstra
    • 稠密图适配:邻接矩阵 + 朴素 \(O(n^2)\) 实现,在 \(n \le 2500\) 时比堆优化更简洁
    • 适用于"正权无向图单源最短路"类问题,核心是贪心确定最短距离 + 松弛更新

【解题思路】

【算法标签】

普及 #Dijkstra

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int d[2505], vis[2505], e[5005][5005];
int n, m;
void dij(int s)
{
    for (int i=0; i<=n; i++) {  // 将n个点到s的距离都设置为最大值
        d[i] = 1e9;
    }
    d[s] = 0;  // 将源点到自己的距离设置为0
    for (int i=1; i<n; i++) {  // 枚举n-1个顶点
        int u = 0;  // 设计u节点,用来记录要出圈的节点
        for (int j=1; j<=n; j++) {  // 枚举n个顶点
            if (!vis[j] && d[j]<d[u]) {  // 如果这个点没有标记过,找到到源点最小距离的点
                u = j;  // 使用查找并交换的方式找
            }
        }
        vis[u] = 1;  // 标记到源点最小距离的点已出圈
        for (int j=1; j<=n; j++) {  // 遍历n个顶点
            d[j] = min(d[j], d[u]+e[u][j]);  // 求每个点到源点之前计算的距离,和出圈的点到源点的距离加上邻边的权重的最小值(如果某个点与u没有邻接关系,即为无穷大,这样保留之前到源点的距离)
        }
    }
}
int main()
{
    int s, t;
    memset(e, 0x3f, sizeof(e));
    cin >> n >> m >> s >> t;
    for (int i=0; i<m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        e[u][v] = w;
        e[v][u] = w;
    }
    dij(t);
    cout << d[s] << endl;
    return 0;
}

【运行结果】

7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1
7
posted @ 2026-08-27 11:06  团爸讲算法  阅读(2)  评论(0)    收藏  举报