给你一个无向图(原始图),图中有 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
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法:Dijkstra 算法
思路
将原始图作为加权无向图处理,我们可以使用 Dijkstra 算法查找原始图中的所有可到达结点。 但是,这不足以解决仅部分使用细分边的示例。
当我们沿着边(沿任一方向)行进时,我们可以跟踪我们使用它的程度。最后,我们想知道我们在原始图中到达的每个结点,以及每条边的利用率之和。
算法
我们使用 Dijkstra 算法 来找出从源到所有目标的最短距离。 这是一个教科书算法, 请参阅此链接了解详细信息。
另外,对于每条(有向)边 (node,nei),我们将跟踪有多少新结点(从原始边细分而来的新结点)被使用。 最后,我们将总结每条边的利用率。
有关更多详细信息,请参阅内联注释。
作者:LeetCode
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/xi-fen-tu-zhong-de-ke-dao-da-jie-dian-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public int reachableNodes(int[][] edges, int M, int N) {
Map> graph = new HashMap();
for (int[] edge: edges) {
int u = edge[0], v = edge[1], w = edge[2];
graph.computeIfAbsent(u, x->new HashMap()).put(v, w);
graph.computeIfAbsent(v, x->new HashMap()).put(u, w);
}
PriorityQueue pq = new PriorityQueue(
(a, b) -> Integer.compare(a.dist, b.dist));
pq.offer(new ANode(0, 0));
Map dist = new HashMap();
dist.put(0, 0);
Map used = new HashMap();
int ans = 0;
while (!pq.isEmpty()) {
ANode anode = pq.poll();
int node = anode.node;
int d = anode.dist;
if (d > dist.getOrDefault(node, 0)) continue;
// Each node is only visited once. We've reached
// a node in our original graph.
ans++;
if (!graph.containsKey(node)) continue;
for (int nei: graph.get(node).keySet()) {
// M - d is how much further we can walk from this node;
// weight is how many new nodes there are on this edge.
// v is the maximum utilization of this edge.
int weight = graph.get(node).get(nei);
int v = Math.min(weight, M - d);
used.put(N * node + nei, v);
// d2 is the total distance to reach 'nei' (nei***or) node
// in the original graph.
int d2 = d + weight + 1;
if (d2 < dist.getOrDefault(nei, M+1)) {
pq.offer(new ANode(nei, d2));
dist.put(nei, d2);
}
}
}
// At the end, each edge (u, v, w) can be used with a maximum
// of w new nodes: a max of used[u, v] nodes from one side,
// and used[v, u] nodes from the other.
// [We use the encoding (u, v) = u * N + v.]
for (int[] edge: edges) {
ans += Math.min(edge[2], used.getOrDefault(edge[0] * N + edge[1], 0) +
used.getOrDefault(edge[1] * N + edge[0], 0) );
}
return ans;
}
}
class ANode {
int node, dist;
ANode(int n, int d) {
node = n;
dist = d;
}
}
作者:LeetCode
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/xi-fen-tu-zhong-de-ke-dao-da-jie-dian-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
先无视图中小点,按Dijkstra得出0到各大点的最短距离。然后考虑从每个大点出发,基于maxMove - distance[i]得出能到达几个小点。
上图中我们称:大点(n个节点),小点(n个节点之间的新节点)
那么小点个数+1,即大点间的距离。参考图中0->1,直线距离是11
无负边无环,通过 Djistra + 限制条件maxMove 得出是否可达 reached[]+最小距离distances[]
然后分别考虑
大点:根据 是否可达,计数: if(reached[i]) ret++;
小点:根据 最小距离,加上抛除maxMove剩余的距离还能达到多少,计数: if(reached[i]) ret+= maxMove-distances[i]。比如上图中的节点1,distances[1] = 5, 意思是0->1需要5步,那么总计不超过maxMove6的前提下,只能再走 maxMove-distances[i] 步
计算小点时,一条边的两个大点分别出发,有可能重复计数,所以 ret+= Math.min(nodeCountBetweenIJ, fromI+fromJ)
时间复杂度 O(ElogE + V), 空间复杂度 O(V)
作者:sodawy
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/javadijkstra-yi-bu-si-lu-bian-huan-by-so-lwfi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
int[] distances;
boolean[] reached;
public int reachableNodes(int[][] edges, int maxMoves, int n) {
//init
distances = new int[n];
reached = new boolean[n];
//build graph
Map> graph = buildGraph(edges, n);
//djistra get shortest and reachable distances
djistra(graph, maxMoves);
//count sub-divdedNodes from the nodes
int ret = 0;
for(int[] edge : edges) {
int from = edge[0], to = edge[1], nodeCount = edge[2];
int sum = 0;
sum += reached[from] ? maxMoves - distances[from] : 0;
sum += reached[to] ? maxMoves - distances[to] : 0;
ret += Math.min(nodeCount, sum);
}
for(int i = 0; i < n; i++) {
if(reached[i]) {
ret++;
}
}
return ret;
}
private void djistra(Map> graph, int maxMoves) {
Arrays.fill(distances, Integer.MAX_VALUE);
PriorityQueue pq = new PriorityQueue<>((a,b)-> a[0] - b[0]);
pq.offer(new int[]{0, 0});
distances[0] = 0;
while(!pq.isEmpty()) {
int[] node = pq.poll();
int curD = node[0], from = node[1];
if(reached[from]) continue;
reached[from] = true;
//firstTime we got `from`, curD is the shortest distance to `source`
distances[from] = curD;
Map nextMap = graph.get(from);
for(int to : nextMap.keySet()) {
if(reached[to]) continue;
if(curD + nextMap.get(to) <= maxMoves) {
pq.offer(new int[]{curD + nextMap.get(to), to});
}
}
}
}
// from, to>>
private Map> buildGraph(int[][] edges, int n) {
Map> graph = new HashMap<>();
for(int i = 0; i < n; i++) {
graph.put(i, new HashMap<>());
}
for(int[] edge : edges) {
int from = edge[0];
int to = edge[1];
int count = edge[2] + 1;
graph.get(from).put(to, count);
graph.get(to).put(from, count);
}
return graph;
}
}
作者:sodawy
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/javadijkstra-yi-bu-si-lu-bian-huan-by-so-lwfi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。