AIGC标识 【算法】【学习笔记】最短路问题

偷懒得很,大部分都使用了AI精修。

最短路问题一般有3种解法:

  • 无权图直接用 BFS;
  • 有权正边图用 Dijkstra;
  • 动态规划;
  • 游戏大地图/寻路系统优先选 A*。

解题算法

Dijkstra算法

Dijkstra 算法的核心思想是贪心 + 边松弛:每次选出当前未确定最短路中累积距离最小的节点,利用它去更新其邻居节点的距离,直到所有节点都处理完毕。

核心要素与机制

  • 距离数组 dist[]:记录从起点到每个节点的当前已知最短距离。起点设为 0,其余节点初始化为无穷大
  • 小顶堆 / 优先队列(Min-Heap):用于在 \(O(\log V)\) 时间内快速取出当前 dist 值最小的节点。
  • 松弛操作(Relaxation):如果通过节点 \(u\) 到达邻居 \(v\) 的距离更短,即 dist[u] + weight < dist[v],则更新 dist[v] = dist[u] + weight 并将 \((dist[v], v)\) 存入堆中。

具体执行步骤

  1. 初始化dist[start] = 0,将 (0, start) 压入小顶堆
  2. 弹出当前最近节点:从小顶堆弹出当前累计距离最小的节点 \(u\) 及其距离 \(d\)
  3. 去重检查:如果 \(d > dist[u]\),说明它是一条已经被更短路径替代的“过期数据”,直接跳过。
  4. 松弛更新:遍历 \(u\) 的所有邻居 \(v\),若 dist[u] + weight(u, v) < dist[v],更新 dist[v] 并将 (dist[v], v) 入堆。
  5. 循环:重复步骤 2~4,直到堆为空。

C++模板

#include <vector>
#include <queue>

using namespace std;

struct Edge {
    int to;
    int weight;
};

vector<int> dijkstra(int n, const vector<vector<Edge>>& graph, int start) {
    vector<int> dist(n, 1e9); // 初始化为无穷大
    
    // 小顶堆:存储 pair<当前距离, 节点编号>
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

    dist[start] = 0;
    pq.push({0, start});

    while (!pq.empty()) {
        auto [d, u] = pq.top();
        pq.pop();

        // 懒删除:如果弹出的距离已经大于当前记录的最短距离,直接跳过
        if (d > dist[u]) continue;

        for (const auto& edge : graph[u]) {
            int v = edge.to;
            int weight = edge.weight;

            // 松弛操作
            if (dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
                pq.push({dist[v], v});
            }
        }
    }

    return dist;
}


复杂度与致命限制

  • 时间复杂度\(O((V + E) \log V)\),其中 \(V\) 是节点数,\(E\) 是边数。
  • 空间复杂度\(O(V + E)\),邻接表及堆空间。
  • 致命限制严禁存在负权边。如果图中有负权边,贪心选择的最短节点后续可能被负权拉得更小,导致算法失效,遇到负权边需改用 Bellman-Ford / SPFA 算法。

❔题目

1. 网络延迟时间

解题思路

这个就是要找每个节点,到k的最短距离,所以使用Dijkstra算法。

  • 单源最短路:信号从单一固定的起点 \(K\) 发出。
  • 边权非负:传递时间 \(w_i\) 为非负数,完全符合 Dijkstra 的使用条件。
  • 问题转化:所有节点都收到信号所需要的总时间,等价于信号到达离起点 \(K\) 最远的那个节点所消耗的时间。

即用 Dijkstra 求出 \(K\) 到各个节点的最短距离 dist 数组后,取其中的最大值 \(\max(\text{dist})\)。如果有任何节点无法到达(dist 仍为无穷大),则返回 -1。

注意:节点编号是从 \(1\)\(n\),建图和分配数组大小时需开到 n + 1。

实现

function networkDelayTime(times: number[][], n: number, k: number): number {
    // 1. 建图:邻接表 graph[u] = [[v, w], ...]
    const graph: [number, number][][] = Array.from({ length: n + 1 }, () => []);
    for (const [u, v, w] of times) {
        graph[u].push([v, w]);
    }

    // 2. 初始化距离数组为无穷大
    const dist = new Array(n + 1).fill(Infinity);
    dist[k] = 0;

    // 3. 朴素/小顶堆 Dijkstra
    // 队列中存 [节点编号, 当前距离]
    const pq = new MinPriorityQueue<[number, number]>({
        compare: (a, b) => a[1] - b[1]
    });
    pq.enqueue([k, 0]);

    while (!pq.isEmpty()) {
        const [u, d] = pq.dequeue();

        if (d > dist[u]) continue;

        for (const [v, w] of graph[u]) {
            if (dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                pq.enqueue([v, dist[v]]);
            }
        }
    }

    // 4. 获取 1 到 n 节点中最长的时间
    let maxDist = 0;
    for (let i = 1; i <= n; i++) {
        if (dist[i] === Infinity) return -1;
        maxDist = Math.max(maxDist, dist[i]);
    }

    return maxDist;
}
public class Solution {
    public int NetworkDelayTime(int[][] times, int n, int k) {
        // 1. 建图:邻接表
        var graph = new List<(int to, int weight)>[n + 1];
        for (int i = 1; i <= n; i++) {
            graph[i] = new List<(int, int)>();
        }

        foreach (var t in times) {
            graph[t[0]].Add((t[1], t[2]));
        }

        // 2. 初始化距离数组
        int[] dist = new int[n + 1];
        Array.Fill(dist, int.MaxValue / 2); // 避免加法溢出

        // 3. 优先队列 (小顶堆):PriorityQueue<Element, Priority>
        var pq = new PriorityQueue<int, int>();
        dist[k] = 0;
        pq.Enqueue(k, 0);

        while (pq.Count > 0) {
            pq.TryDequeue(out int u, out int d);

            if (d > dist[u]) continue;

            foreach (var edge in graph[u]) {
                int v = edge.to;
                int w = edge.weight;

                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    pq.Enqueue(v, dist[v]);
                }
            }
        }

        // 4. 寻找最大延时
        int maxDist = 0;
        for (int i = 1; i <= n; i++) {
            if (dist[i] >= int.MaxValue / 2) return -1;
            maxDist = Math.Max(maxDist, dist[i]);
        }

        return maxDist;
    }
}
class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        // 1. 建图:邻接表
        vector<vector<pair<int, int>>> graph(n + 1);
        for (const auto& t : times) {
            graph[t[0]].push_back({t[1], t[2]});
        }

        // 2. 初始化距离数组与小顶堆
        vector<int> dist(n + 1, 1e9);
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

        dist[k] = 0;
        pq.push({0, k}); // pair<当前距离, 节点编号>

        // 3. Dijkstra 过程
        while (!pq.empty()) {
            auto [d, u] = pq.top();
            pq.pop();

            if (d > dist[u]) continue;

            for (const auto& edge : graph[u]) {
                int v = edge.first;
                int w = edge.second;

                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    pq.push({dist[v], v});
                }
            }
        }

        // 4. 统计结果
        int maxDist = 0;
        for (int i = 1; i <= n; ++i) {
            if (dist[i] == 1e9) return -1;
            maxDist = max(maxDist, dist[i]);
        }

        return maxDist;
    }
};

复杂度分析

  • 时间复杂度\(O((V + E) \log V)\)
    其中 \(V = n\)\(E\) 为 times 数组的长度。
  • 空间复杂度\(O(V + E)\)
    用于存储邻接表、dist 数组以及优先队列。

2. 包含要求路径的最小带权子图

解题思路

原始思路:
用广度搜索,为src1和scr2搜索路,然后结合成图。

但是这是不行的,广度搜索无法处理带权最短路,这里应该使用Dijkstra算法。

为了让总边权最小,从 \(src1\)\(src2\) 出发的两条路径应该尽可能多地重叠

我们可以假设它们在某个中间节点 \(m\)\(m\) 可以是图中任意节点,包括起点或终点本身)相遇,然后合并为一条路径共同前往 \(dest\)

此时总开销公式为:

\[\text{Total Cost}(m) = \text{dist}(src1 \to m) + \text{dist}(src2 \to m) + \text{dist}(m \to dest) \]

只需要遍历所有可能的汇合点 \(m \in [0, n-1]\),求出上述三项之和的最小值即可。

可以看出我们要搜索三项距离,所以使用 3 次 Dijkstra。

  • 正向图 \(G\):运行 Dijkstra 从 \(src1\) 出发,得到数组 \(d_1\),其中 \(d_1[m]\) 表示 \(src1 \to m\) 的最短距离。
  • 正向图 \(G\):运行 Dijkstra 从 \(src2\) 出发,得到数组 \(d_2\),其中 \(d_2[m]\) 表示 \(src2 \to m\) 的最短距离。
  • ⭐反向图 \(G^R\):将原图的所有有向边反向(\(u \to v\) 变成 \(v \to u\)),然后运行 Dijkstra 从 \(dest\) 出发,得到数组 \(d_{dest}\)。其中 \(d_{dest}[m]\) 刚好等于原图中 \(m \to dest\) 的最短距离!

注意:由于边权可能累加很大(最大可达 \(10^{10}\)),C# 和 C++ 中必须使用 64位整数(long / long long) 存储距离,防止数据溢出。

实现

function minimumWeight(n: number, edges: number[][], src1: number, src2: number, dest: number): number {
    // 1. 建正向图 g 和反向图 rg
    const g: [number, number][][] = Array.from({ length: n }, () => []);
    const rg: [number, number][][] = Array.from({ length: n }, () => []);

    for (const [u, v, w] of edges) {
        g[u].push([v, w]);
        rg[v].push([u, w]); // 边的方向反转
    }

    // 通用 Dijkstra 函数
    function dijkstra(start: number, graph: [number, number][][]): number[] {
        const dist = new Array(n).fill(Infinity);
        dist[start] = 0;

        const pq = new MinPriorityQueue<[number, number]>({ compare: (a, b) => a[1] - b[1] });
        pq.enqueue([start, 0]);

        while (!pq.isEmpty()) {
            const [u, d] = pq.dequeue();
            if (d > dist[u]) continue;

            for (const [v, w] of graph[u]) {
                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    pq.enqueue([v, dist[v]]);
                }
            }
        }
        return dist;
    }

    // 2. 跑三次 Dijkstra
    const d1 = dijkstra(src1, g);
    const d2 = dijkstra(src2, g);
    const dDest = dijkstra(dest, rg);

    // 3. 枚举所有可能的汇合点 m
    let ans = Infinity;
    for (let m = 0; m < n; m++) {
        if (d1[m] !== Infinity && d2[m] !== Infinity && dDest[m] !== Infinity) {
            ans = Math.min(ans, d1[m] + d2[m] + dDest[m]);
        }
    }

    return ans === Infinity ? -1 : ans;
}
public class Solution {
    public long MinimumWeight(int n, int[][] edges, int src1, int src2, int dest) {
        var g = new List<(int to, int weight)>[n];
        var rg = new List<(int to, int weight)>[n];
        for (int i = 0; i < n; i++) {
            g[i] = new List<(int, int)>();
            rg[i] = new List<(int, int)>();
        }

        foreach (var e in edges) {
            g[e[0]].Add((e[1], e[2]));
            rg[e[1]].Add((e[0], e[2]));
        }

        long[] Dijkstra(int start, List<(int to, int weight)>[] graph) {
            long[] dist = new long[n];
            Array.Fill(dist, long.MaxValue / 2);
            var pq = new PriorityQueue<int, long>();

            dist[start] = 0;
            pq.Enqueue(start, 0);

            while (pq.Count > 0) {
                pq.TryDequeue(out int u, out long d);
                if (d > dist[u]) continue;

                foreach (var edge in graph[u]) {
                    int v = edge.to;
                    int w = edge.weight;
                    if (dist[u] + w < dist[v]) {
                        dist[v] = dist[u] + w;
                        pq.Enqueue(v, dist[v]);
                    }
                }
            }
            return dist;
        }

        long[] d1 = Dijkstra(src1, g);
        long[] d2 = Dijkstra(src2, g);
        long[] dDest = Dijkstra(dest, rg);

        long INF = long.MaxValue / 2;
        long ans = INF;

        for (int m = 0; m < n; m++) {
            if (d1[m] < INF && d2[m] < INF && dDest[m] < INF) {
                ans = Math.Min(ans, d1[m] + d2[m] + dDest[m]);
            }
        }

        return ans >= INF ? -1 : ans;
    }
}
class Solution {
public:
    long long minimumWeight(int n, vector<vector<int>>& edges, int src1, int src2, int dest) {
        vector<vector<pair<int, int>>> g(n), rg(n);
        for (const auto& e : edges) {
            g[e[0]].push_back({e[1], e[2]});
            rg[e[1]].push_back({e[0], e[2]});
        }

        const long long INF = 1e18;

        auto dijkstra = [&](int start, const vector<vector<pair<int, int>>>& graph) {
            vector<long long> dist(n, INF);
            priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
            
            dist[start] = 0;
            pq.push({0, start});

            while (!pq.empty()) {
                auto [d, u] = pq.top();
                pq.pop();

                if (d > dist[u]) continue;

                for (const auto& [v, w] : graph[u]) {
                    if (dist[u] + w < dist[v]) {
                        dist[v] = dist[u] + w;
                        pq.push({dist[v], v});
                    }
                }
            }
            return dist;
        };

        auto d1 = dijkstra(src1, g);
        auto d2 = dijkstra(src2, g);
        auto dDest = dijkstra(dest, rg);

        long long ans = INF;
        for (int m = 0; m < n; ++m) {
            if (d1[m] < INF && d2[m] < INF && dDest[m] < INF) {
                ans = min(ans, d1[m] + d2[m] + dDest[m]);
            }
        }

        return ans == INF ? -1 : ans;
    }
};

复杂度分析

  • 时间复杂度\(O((V + E) \log V)\)
    三次 Dijkstra 的时间复杂度均为 \(O((V + E) \log V)\),后续遍历 \(n\) 个汇合点仅需 \(O(V)\),完全能通过大数据量。
  • 空间复杂度\(O(V + E)\)
    用于构建正反向邻接表与保存三次 Dijkstra 的结果数组。

3. 设计可以求最短路径的图类

解题思路

原始思路:
直接每次找最短路时使用dijkstra算法。但这个方法并不快。

使用 Floyd-Warshall 算法(弗洛伊德算法),并结合了动态增量更新(Incremental Update)技巧,用于求解全源最短路径(All-Pairs Shortest Path)。

其核心机制分为三部分:

  • 全局预计算(constructor):
    • 使用 \(O(n^3)\) 的三重循环动态规划,求出图中任意两点之间的最短距离矩阵 g。
    • 代码里的外层循环 i 充当中间过渡节点(即经典的 \(k\) 节点),状态转移方程为:

    \[g[j][k] = \min(g[j][k], g[j][i] + g[i][k]) \]

  • 动态加边增量更新(addEdge):
    • 当插入一条新边 \(u \to v\)(权重为 \(w\))时,无需重新执行 \(O(n^3)\) 的完整算法,而是只需 \(O(n^2)\) 时间检查所有点对 \((i, j)\)
    • 若从 \(i \to u \to v \to j\) 的路径比原有路径更短,则直接更新:$$g[i][j] = \min(g[i][j], g[i][u] + w + g[v][j])$$
  • 常数级查询(shortestPath):
    • 由于之前已经计算并维护好了所有点对的最短距离,查询时直接查表返回 g[node1][node2],时间复杂度为 \(O(1)\)

适用场景:节点数量较小(如 \(n \le 100\))、需要频繁添加边,且需要极速响应两点间最短路查询的情况。

实现

class Graph {
    private n: number;
    private dist: number[][];
    private readonly INF = 1e9;

    constructor(n: number, edges: number[][]) {
        this.n = n;
        this.dist = Array.from({ length: n }, () => new Array(n).fill(this.INF));

        for (let i = 0; i < n; i++) {
            this.dist[i][i] = 0;
        }

        for (const [u, v, w] of edges) {
            this.dist[u][v] = Math.min(this.dist[u][v], w);
        }

        // Floyd-Warshall O(n^3)
        for (let k = 0; k < n; k++) {
            for (let i = 0; i < n; i++) {
                if (this.dist[i][k] === this.INF) continue; // 此时i到k还没有路,剪枝
                for (let j = 0; j < n; j++) {
                    this.dist[i][j] = Math.min(this.dist[i][j], this.dist[i][k] + this.dist[k][j]);
                }
            }
        }
    }

    addEdge(edge: number[]): void {
        const [u, v, w] = edge;
        if (w >= this.dist[u][v]) return; // 新边权不优,直接忽略

        for (let i = 0; i < this.n; i++) {
            // 剪枝 1:如果 i 到不了 u,直接跳过内层对 j 的所有循环
            if (this.dist[i][u] === this.INF) continue; 

            for (let j = 0; j < this.n; j++) {
                // 剪枝 2:如果 v 到不了 j,跳过本次更新
                if (this.dist[v][j] === this.INF) continue; 

                // 安全更新
                if (this.dist[i][u] + w + this.dist[v][j] < this.dist[i][j]) {
                    this.dist[i][j] = this.dist[i][u] + w + this.dist[v][j];
                }
            }
        }
    }

    shortestPath(node1: number, node2: number): number {
        const res = this.dist[node1][node2];
        return res >= this.INF ? -1 : res; // O(1) 查询
    }
}
public class Graph {
    private int n;
    private int[,] dist;
    private const int INF = 1_000_000_000;

    public Graph(int n, int[][] edges) {
        this.n = n;
        dist = new int[n, n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dist[i, j] = (i == j) ? 0 : INF;
            }
        }

        foreach (var edge in edges) {
            int u = edge[0], v = edge[1], w = edge[2];
            dist[u, v] = Math.Min(dist[u, v], w);
        }

        // Floyd-Warshall O(n^3)
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                if (dist[i, k] == INF) continue; // 剪枝
                for (int j = 0; j < n; j++) {
                    dist[i, j] = Math.Min(dist[i, j], dist[i, k] + dist[k, j]);
                }
            }
        }
    }

    public void AddEdge(int[] edge) {
        int u = edge[0], v = edge[1], w = edge[2];
        if (w >= dist[u, v]) return;

        // 动态增量更新 O(n^2)
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dist[i, j] = Math.Min(dist[i, j], dist[i, u] + w + dist[v, j]);
            }
        }
    }

    public int ShortestPath(int node1, int node2) {
        return dist[node1, node2] >= INF ? -1 : dist[node1, node2]; // O(1) 查询
    }
}
class Graph {
private:
    int n;
    vector<vector<int>> dist;
    const int INF = 1e9;

public:
    Graph(int n, vector<vector<int>>& edges) : n(n) {
        dist.assign(n, vector<int>(n, INF));
        for (int i = 0; i < n; ++i) {
            dist[i][i] = 0;
        }
        for (const auto& edge : edges) {
            int u = edge[0], v = edge[1], w = edge[2];
            dist[u][v] = min(dist[u][v], w);
        }

        // Floyd-Warshall O(n^3)
        for (int k = 0; k < n; ++k) {
            for (int i = 0; i < n; ++i) {
                if (dist[i][k] == INF) continue; // 剪枝
                for (int j = 0; j < n; ++j) {
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
    }

    void addEdge(vector<int> edge) {
        int u = edge[0], v = edge[1], w = edge[2];
        if (w >= dist[u][v]) return;

        // 动态增量更新 O(n^2)
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                dist[i][j] = min(dist[i][j], dist[i][u] + w + dist[v][j]);
            }
        }
    }

    int shortestPath(int node1, int node2) {
        return dist[node1][node2] >= INF ? -1 : dist[node1][node2]; // O(1) 查询
    }
};

复杂度分析

操作 时间复杂度 空间复杂度 说明
constructor \(O(n^3)\) \(O(n^2)\) 三重循环遍历 \((k, i, j)\) 预计算全图最短路;开辟 \(n \times n\) 距离矩阵。
addEdge \(O(n^2)\) \(O(1)\) 只需要双重循环遍历所有节点对 \((i, j)\),尝试用新边做松弛更新,与当前有多少条边无关。
shortestPath \(O(1)\) \(O(1)\) 直接查表 dist[node1][node2],常数级响应。

4. 阈值距离内邻居最少的城市

解题思路

原思路:
需要一个dist[][]数组,存放每个城市在某个距离里可以到的城市数啊,用动态规划。
但这很明显写不动。

dist[i][j] 存城市 \(i\) 到城市 \(j\)最短路径距离
建立出全图的最短距离矩阵后,统计每行满足 dist[i][j] <= distanceThreshold 的数量,就能找出目标城市。
因为题目限制 \(n \le 100\),用动态规划思想的 Floyd-Warshall 算法 预处理全图最短路是最方便的做法。

解题步骤

  • 构建距离矩阵:
    初始化 \(n \times n\) 的 dist 数组,对角线 dist[i][i] = 0,其余设为无穷大 INF。
  • 填充双向边:
    遍历 edges,填入双向权值 dist[u][v] = dist[v][u] = w。
  • Floyd 动态规划:
    三重循环求任意两点间的最短路:dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])。
  • 统计与筛选:
    遍历每个城市 \(i\),统计满足 dist[i][j] <= distanceThreshold 的城市 \(j\) 数量(\(j \neq i\))。更新邻居数量最少的城市;若数量相等,更新为编号更大的城市(即遍历时取 count <= minCount)。

实现

function findTheCity(n: number, edges: number[][], distanceThreshold: number): number {
    const INF = 1e9;
    const dist: number[][] = Array.from({ length: n }, () => new Array(n).fill(INF));

    for (let i = 0; i < n; i++) dist[i][i] = 0;

    // 填充无向图邻接矩阵
    for (const [u, v, w] of edges) {
        dist[u][v] = w;
        dist[v][u] = w;
    }

    // 1. Floyd-Warshall DP 求任意两点最短路
    for (let k = 0; k < n; k++) {
        for (let i = 0; i < n; i++) {
            for (let j = 0; j < n; j++) {
                dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
            }
        }
    }

    // 2. 统计每个城市在阈值内的邻居数
    let minCount = INF;
    let ans = -1;

    for (let i = 0; i < n; i++) {
        let count = 0;
        for (let j = 0; j < n; j++) {
            if (i !== j && dist[i][j] <= distanceThreshold) {
                count++;
            }
        }
        // count <= minCount 确保了数量相同时,优先选择编号更大的城市 i
        if (count <= minCount) {
            minCount = count;
            ans = i;
        }
    }

    return ans;
}
public class Solution {
    public int FindTheCity(int n, int[][] edges, int distanceThreshold) {
        const int INF = 1_000_000_000;
        int[,] dist = new int[n, n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dist[i, j] = (i == j) ? 0 : INF;
            }
        }

        foreach (var e in edges) {
            dist[e[0], e[1]] = e[2];
            dist[e[1], e[0]] = e[2];
        }

        // Floyd-Warshall
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    dist[i, j] = Math.Min(dist[i, j], dist[i, k] + dist[k, j]);
                }
            }
        }

        int minCount = INF;
        int ans = -1;

        for (int i = 0; i < n; i++) {
            int count = 0;
            for (int j = 0; j < n; j++) {
                if (i != j && dist[i, j] <= distanceThreshold) {
                    count++;
                }
            }
            if (count <= minCount) {
                minCount = count;
                ans = i;
            }
        }

        return ans;
    }
}
class Solution {
public:
    int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
        const int INF = 1e9;
        vector<vector<int>> dist(n, vector<int>(n, INF));

        for (int i = 0; i < n; ++i) dist[i][i] = 0;

        for (const auto& e : edges) {
            dist[e[0]][e[1]] = e[2];
            dist[e[1]][e[0]] = e[2];
        }

        // Floyd-Warshall
        for (int k = 0; k < n; ++k) {
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }

        int minCount = INF;
        int ans = -1;

        for (int i = 0; i < n; ++i) {
            int count = 0;
            for (int j = 0; j < n; ++j) {
                if (i != j && dist[i][j] <= distanceThreshold) {
                    count++;
                }
            }
            if (count <= minCount) {
                minCount = count;
                ans = i;
            }
        }

        return ans;
    }
};

复杂度分析

  • 时间复杂度\(O(n^3)\)
    三重循环预处理最短距离。
  • 空间复杂度\(O(n^2)\)
    构建 \(n \times n\) 的距离矩阵。

引用

[1] 力扣探索模式


注:本文为个人学习与刷题笔记,部分文本结构与排版格式由 AI 辅助整理。

posted @ 2026-08-25 16:04  SEHOD  阅读(5)  评论(0)    收藏  举报