细分图中的可到达节点
难度:
标签:
题目描述
给你一个无向图(原始图),图中有 n
个节点,编号从 0
到 n - 1
。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。
图用由边组成的二维数组 edges
表示,其中 edges[i] = [ui, vi, cnti]
表示原始图中节点 ui
和 vi
之间存在一条边,cnti
是将边 细分 后的新节点总数。注意,cnti == 0
表示边不可细分。
要 细分 边 [ui, vi]
,需要将其替换为 (cnti + 1)
条新边,和 cnti
个新节点。新节点为 x1
, x2
, ..., xcnti
,新边为 [ui, x1]
, [x1, x2]
, [x2, x3]
, ..., [xcnti-1, xcnti]
, [xcnti, vi]
。
现在得到一个 新的细分图 ,请你计算从节点 0
出发,可以到达多少个节点?如果节点间距离是 maxMoves
或更少,则视为 可以到达 。
给你原始图和 maxMoves
,返回 新的细分图中从节点 0
出发 可到达的节点数 。
示例 1:
输入:edges = [[0,1,10],[0,2,1],[1,2,2]], maxMoves = 6, n = 3 输出:13 解释:边的细分情况如上图所示。 可以到达的节点已经用黄色标注出来。
示例 2:
输入:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], maxMoves = 10, n = 4 输出:23
示例 3:
输入:edges = [[1,2,4],[1,4,5],[1,3,1],[2,3,4],[3,4,5]], maxMoves = 17, n = 5 输出:1 解释:节点 0 与图的其余部分没有连通,所以只有节点 0 可以到达。
提示:
0 <= edges.length <= min(n * (n - 1) / 2, 104)
edges[i].length == 3
0 <= ui < vi < n
- 图中 不存在平行边
0 <= cnti <= 104
0 <= maxMoves <= 109
1 <= n <= 3000
代码结果
运行时间: 117 ms, 内存: 21.6 MB
/**
* Similar to the Java solution, but with the use of Java Streams for constructing the graph and processing the edges.
* Streams are used to create and process data in a more functional programming style.
*/
import java.util.*;
import java.util.stream.Collectors;
public class ReachableNodesStream {
public int reachableNodes(int[][] edges, int maxMoves, int n) {
// Create the graph using Streams
Map<Integer, Map<Integer, Integer>> graph = Arrays.stream(edges)
.collect(Collectors.groupingBy(edge -> edge[0], Collectors.toMap(edge -> edge[1], edge -> edge[2])));
graph.putAll(Arrays.stream(edges)
.collect(Collectors.groupingBy(edge -> edge[1], Collectors.toMap(edge -> edge[0], edge -> edge[2]))));
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
pq.offer(new int[]{0, 0});
Map<Integer, Integer> visited = new HashMap<>();
visited.put(0, 0);
int result = 0;
while (!pq.isEmpty()) {
int[] cur = pq.poll();
int curMoves = cur[0], curNode = cur[1];
if (curMoves > visited.getOrDefault(curNode, 0)) continue;
result++;
Map<Integer, Integer> neighbors = graph.getOrDefault(curNode, Collections.emptyMap());
result += neighbors.entrySet().stream()
.mapToInt(entry -> {
int nextNode = entry.getKey(), cnt = entry.getValue();
int nextMoves = curMoves + cnt + 1;
if (nextMoves <= maxMoves && nextMoves < visited.getOrDefault(nextNode, Integer.MAX_VALUE)) {
pq.offer(new int[]{nextMoves, nextNode});
visited.put(nextNode, nextMoves);
}
return Math.min(cnt, maxMoves - curMoves);
}).sum();
}
return result;
}
}
解释
方法:
本题解采用 Dijkstra 算法来计算从起始节点0到其他所有节点的最短路径。首先构建图,将原图的每条边细分成新的节点和边,然后使用 Dijkstra 算法找出从节点0到任何其他节点的最短距离。对每个节点,如果它可以在 maxMoves 或更少的移动中到达,则增加可到达的节点数。对于每条边,根据两端节点的最短路径和中间节点的数量,计算额外可以访问的中间节点数。综合考虑这些因素,最终得出所有可以访问的节点总数。
时间复杂度:
O(E log V)
空间复杂度:
O(V + E)
代码细节讲解
🦆
在构建图的过程中,你是如何处理细分后的新节点和新边的?题解中提到直接增加权重,这是否意味着新节点没有显式表现在图的结构中?
▷🦆
Dijkstra算法通常用于处理带权重的最短路径问题,细分后的边权重是怎样计算的?具体地,`c+1`这个权重的计算依据是什么?
▷🦆
题解中提到,使用最小堆来优化Dijkstra算法的效率,能否解释一下最小堆在这里是如何起到优化作用的?
▷🦆
题解涉及到计算通过边细分可能额外访问的节点数量,如何确保在`min(dis[u], dis[v]) + c <= maxMoves`的情况下正确计算这些额外节点?
▷