力扣题解3243 新增道路查询后的最短距离 I

题目:

给你一个整数 n 和一个二维整数数组 queries。

有 n 个城市,编号从 0 到 n - 1。初始时,每个城市 i 都有一条单向道路通往城市 i + 1( 0 <= i < n - 1)。

queries[i] = [ui, vi] 表示新建一条从城市 ui 到城市 vi 的单向道路。每次查询后,你需要找到从城市 0 到城市 n - 1 的最短路径的长度。

返回一个数组 answer,对于范围 [0, queries.length - 1] 中的每个 i,answer[i] 是处理完前 i +1 个查询后,从城市 0 到城市 n - 1 的最短路径的长度。

1. 题目重述及关键字提取

题目重述 :

给定一个整数 n 和一个二维整数数组 queries,表示有 n 个城市,城市编号从 0n - 1。每个城市 i 有一条单向道路通往城市 i + 1。对于每个查询 queries[i] = [ui, vi],意味着新建一条从城市 ui 到城市 vi 的单向道路。每次查询后,需要计算从城市 0 到城市 n - 1 的最短路径长度,并返回这些长度的数组。

关键字提取:

  • 城市 (cities)
  • 单向道路 (directed roads)
  • 最短路径 (shortest path)
  • 查询 (queries)
  • 数组结果 (result array)

2. 解题思路与分析

方法一:广度优先遍历 (BFS)

思路 :

广度优先搜索是解决最短路径问题的有效方式,尤其是在图的边权均为 1 的情况下(每条边的权重相同)。首先,构建一个图结构来表示城市和道路,然后从城市 0 开始进行 BFS,记录到每个城市的最短距离。

步骤:

  1. 初始化一个图的邻接表。
  2. 将初始的单向道路添加到图中。
  3. 对于每个查询,将新道路添加到图中。
  4. 使用 BFS 从城市 0 开始,寻找最短路径到城市 n - 1
  5. 返回每一步的最短路径长度。
C++代码(广度优先搜索)
cpp 复制代码
vector<int> shortestDistanceAfterQueries(int n, vector<vector<int>>& queries) {
    unordered_map<int, vector<int>> graph;
    
    // Initialize the graph with initial roads
    for (int i = 0; i < n - 1; ++i) {
        graph[i].push_back(i + 1);
    }

    vector<int> answer;
    for (auto& query : queries) {
        int u = query[0], v = query[1];
        graph[u].push_back(v);  // Add the new road

        // BFS to find the shortest path from 0 to n-1
        vector<int> dist(n, -1);
        dist[0] = 0;
        queue<int> q;
        q.push(0);

        while (!q.empty()) {
            int node = q.front(); q.pop();
            for (int neighbor : graph[node]) {
                if (dist[neighbor] == -1) {  // Not visited
                    dist[neighbor] = dist[node] + 1;
                    q.push(neighbor);
                }
            }
        }

        answer.push_back(dist[n - 1]);
    }

    return answer;
}
Java代码(广度优先搜索)
java 复制代码
public class Solution {
    public int[] shortestDistanceAfterQueries(int n, int[][] queries) {
        Map<Integer, List<Integer>> graph = new HashMap<>();
        
        // Initialize the graph with initial roads
        for (int i = 0; i < n - 1; i++) {
            graph.putIfAbsent(i, new ArrayList<>());
            graph.get(i).add(i + 1);
        }

        int[] answer = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            int u = queries[i][0], v = queries[i][1];
            graph.putIfAbsent(u, new ArrayList<>());
            graph.get(u).add(v);  // Add the new road

            // BFS to find the shortest path from 0 to n-1
            int[] dist = new int[n];
            Arrays.fill(dist, -1);
            dist[0] = 0;

            Queue<Integer> queue = new LinkedList<>();
            queue.add(0);

            while (!queue.isEmpty()) {
                int node = queue.poll();
                for (int neighbor : graph.getOrDefault(node, Collections.emptyList())) {
                    if (dist[neighbor] == -1) {  // Not visited
                        dist[neighbor] = dist[node] + 1;
                        queue.add(neighbor);
                    }
                }
            }

            answer[i] = dist[n - 1];
        }

        return answer;
    }
}
方法二:动态规划 (Dynamic Programming)

思路 :

在动态规划中,我们维护一个数组 dpdp[i] 表示从城市 0 到城市 i 的最短距离。初始时,起点城市 0 的距离设置为 0,其他城市的距离设置为无穷大(表示尚未到达)。通过逐步处理每个查询,我们可以利用新添加的道路更新各个城市的最短距离。

步骤:

  1. 初始化 dp 数组:

    • 创建一个大小为 n 的数组 dp,初始化为无穷大(numeric_limits<int>::max())。
    • 设置 dp[0] = 0,表示从城市 0 到自身的距离为 0
  2. 初始化前驱信息:

    • 为了便于后续更新,维护一个二维数组 prevprev[i] 存储所有可以到达城市 i 的前驱城市。
    • 对于每个城市 i(从 1n-1),将城市 i-1 添加为它的前驱,并初始设置 dp[i] = i,这代表最坏情况下通过 i-1 次到达城市 i
  3. 处理每个查询:

    • 对于每个查询 [u, v],将城市 u 添加到城市 v 的前驱列表 prev[v] 中,表示新建了从 uv 的单向道路。
  4. 状态转移:

    • 从城市 v 开始,遍历城市 w(从 vn-1),对于每个城市 w,检查其所有前驱城市 u(从 prev[w] 中获取)。
    • 通过公式 dp[w] = min(dp[w], dp[u] + 1) 更新城市 w 的最短距离。此步骤确保在新道路添加后,所有通过该新道路可到达的城市的最短路径都得到更新。
  5. 记录结果:

    • 在处理完每个查询后,将当前城市 n-1 的最短距离 dp[n - 1] 添加到结果数组 res 中。如果 dp[n - 1] 仍然为无穷大,表示不可达,返回 -1
  6. 返回结果:

    • 返回所有查询的结果数组 res,其中每一项表示在当前查询后从城市 0 到城市 n - 1 的最短距离。
C++代码(动态规划)
cpp 复制代码
vector<int> shortestDistanceAfterQueries(int n, vector<vector<int>> &queries) {  
        vector<vector<int>> prev(n);  
        vector<int> dp(n, numeric_limits<int>::max());  // 使用无穷大进行初始化  
        dp[0] = 0;  // 起点到自身的距离为0  

        // 初始填充前驱信息  
        for (int i = 1; i < n; i++) {  
            prev[i].push_back(i - 1);  
            dp[i] = i; // 最坏情况是 n-1 步到达 i  
        }  
        
        vector<int> res;  
        
        // 处理每个查询  
        for (auto &query : queries) {  
            int u = query[0], v = query[1];  
            prev[v].push_back(u); // 更新前驱  

            // 状态转移  
            for (int w = v; w < n; w++) {  
                for (int u : prev[w]) {  
                    dp[w] = min(dp[w], dp[u] + 1);  
                }  
            }  

            // 记录到最后一座城市的距离  
            res.push_back(dp[n - 1] != numeric_limits<int>::max() ? dp[n - 1] : -1);  
        }  
        
        return res;  
    }  
Java代码(动态规划)
java 复制代码
public int[] shortestDistanceAfterQueries(int n, int[][] queries) {  
        List<List<Integer>> prev = new ArrayList<>();  
        for (int i = 0; i < n; i++) {  
            prev.add(new ArrayList<>());  
        }  
        
        int[] dp = new int[n];  
        Arrays.fill(dp, Integer.MAX_VALUE); // 使用无穷大进行初始化  
        dp[0] = 0; // 起点到自身的距离为0  

        // 初始填充前驱信息  
        for (int i = 1; i < n; i++) {  
            prev.get(i).add(i - 1);  
            dp[i] = i; // 最坏情况是 n-1 步到达 i  
        }  
        
        int[] res = new int[queries.length];  
        
        // 处理每个查询  
        for (int i = 0; i < queries.length; i++) {  
            int u = queries[i][0], v = queries[i][1];  
            prev.get(v).add(u); // 更新前驱  
            
            // 状态转移  
            for (int w = v; w < n; w++) {  
                for (int j : prev.get(w)) {  
                    dp[w] = Math.min(dp[w], dp[j] + 1);  
                }  
            }  

            // 记录到最后一座城市的距离  
            res[i] = dp[n - 1] != Integer.MAX_VALUE ? dp[n - 1] : -1;  
        }  
        
        return res;  
    }

3. 总结分析

广度优先遍历 (BFS) 与动态规划 (Dynamic Programming):

  • 广度优先遍历:

    • BFS 适用于图的最短路径问题,尤其是所有边的权值相同的情况。通过逐层遍历,可以有效找到从起点到终点的最短路径。
  • 动态规划:

    • 动态规划适合在路径长度需要动态更新的情况下,通过不断维护到达每个节点的最小路径,还可以在每次查询后直接更新结果。
相关推荐
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
DogDaoDao3 小时前
leetcode 面试经典 150 题:有效的括号
c++·算法·leetcode·面试··stack·有效的括号
Coovally AI模型快速验证4 小时前
MMYOLO:打破单一模式限制,多模态目标检测的革命性突破!
人工智能·算法·yolo·目标检测·机器学习·计算机视觉·目标跟踪
可为测控5 小时前
图像处理基础(4):高斯滤波器详解
人工智能·算法·计算机视觉
Milk夜雨5 小时前
头歌实训作业 算法设计与分析-贪心算法(第3关:活动安排问题)
算法·贪心算法
BoBoo文睡不醒5 小时前
动态规划(DP)(细致讲解+例题分析)
算法·动态规划
apz_end6 小时前
埃氏算法C++实现: 快速输出质数( 素数 )
开发语言·c++·算法·埃氏算法
仟濹6 小时前
【贪心算法】洛谷P1106 - 删数问题
c语言·c++·算法·贪心算法
银河梦想家7 小时前
【Day23 LeetCode】贪心算法题
leetcode·贪心算法
CM莫问7 小时前
python实战(十五)——中文手写体数字图像CNN分类
人工智能·python·深度学习·算法·cnn·图像分类·手写体识别