一、题目 §
给你一个整数 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
的最短路径的长度。
二、输入 §
城市数和查询数组
三、输出 §
返回一个数组 answer,
对于范围 [0, queries.length - 1]
中的每个 i,answer[i] 是处理完前 i + 1
个查询后,从城市 0 到城市 n - 1
的最短路径的长度。
四、示例 §
输入:
n = 5, queries = [[2, 4], [0, 2], [0, 4]]
输出:
[3, 2, 1]
输入:
n = 4, queries = [[0, 3], [0, 2]]
输出:
[1, 1]
五、题解 §
5.1 Java 实现 §
package org.stone.study.algo.ex202411;
import java.util.*;
/**
* 给你一个整数 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
* 的最短路径的长度。
*/
public class ShortestPaths {
public static void main(String[] args) {
int n = 5;
int[][] queries = {{2, 4}, {0, 2}, {0, 4}};
// int[] ans = new ShortestPaths().shortestDistanceAfterQueries(n, queries);
int[] ans = new ShortestPaths().shortestDistanceAfterQueries2(n, queries);
// [3, 2, 1]
System.out.println(Arrays.toString(ans));
}
public int[] shortestDistanceAfterQueries(int n, int[][] queries) {
// 初始化邻接表
List<Integer>[] neighbors = new ArrayList[n];
for(int i = 0; i < n - 1; i++) {
neighbors[i] = new ArrayList<>();
neighbors[i].add(i + 1);
}
//避免空指针,最后一个也初始化
neighbors[n-1] = new ArrayList<>();
// 每增加一条边重新计算一次最小路径
int m = queries.length;
int[] ans = new int[m];
for(int i = 0; i < m; i++) {
int[] query = queries[i];
neighbors[query[0]].add(query[1]);
ans[i] = bfs(neighbors);
}
return ans;
}
// 广度遍历求最小路径
private int bfs(List<Integer>[] neighbors) {
int n = neighbors.length;
int[] dist = new int[n];
Arrays.fill(dist, -1);
Queue<Integer> queue = new LinkedList<>();
queue.offer(0);
dist[0] = 0;
while(!queue.isEmpty()) {
int cur = queue.poll();
for(int neighbor : neighbors[cur]) {
// 已经计算过的一定是最小的,不用对 dist 进行比较
if (dist[neighbor] < 0) {
queue.offer(neighbor);
dist[neighbor] = dist[cur] + 1;
}
}
}
return dist[n - 1];
}
// 动态规划解法
public int[] shortestDistanceAfterQueries2(int n, int[][] queries) {
int[] ans = new int[queries.length];
int[] dp = new int[n]; // 存储从 0 到 节点i 的最短路径长度
ArrayList<Integer>[] preNodes = new ArrayList[n]; // 存储从 0 到 i 的所有前驱节点
for (int i = 1; i < n; i++) {
dp[i] = i;
preNodes[i] = new ArrayList<Integer>();
preNodes[i].add(i-1);
}
for (int i = 0; i < queries.length; i++) {
int u = queries[i][0], v = queries[i][1];
preNodes[v].add(u);
// 只需要更新从 v 开始到 n-1 的最短路径
for(int j = v; j < n; j++) {
for(int pre : preNodes[j]) {
if (dp[pre] + 1 < dp[j]) {
dp[j] = dp[pre] + 1;
}
}
}
// 本轮的最短路径
ans[i] = dp[n-1];
}
return ans;
}
}
5.2 Python实现 §
def shortPath(n, queries):
# pre[i] 表示i 的所有前驱节点
pre = [[i - 1] for i in range(n)]
pre[0] = []
# dp[i] 表示从 0 到 i 的最短距离
dp = [i for i in range(n)]
ans = []
for (u, v) in queries:
pre[v].append(u)
for j in range(v, n):
for k in pre[j]:
# 从前驱节点中更新最短距离
dp[j] = min(dp[j], dp[k] + 1)
ans.append(dp[n - 1])
return ans
if __name__ == '__main__':
n = 5
queries = [[2, 4], [0, 2], [0, 4]]
ans = shortPath(n, queries)
print(ans)