得到要求路径的最小带权子图
难度:
标签:
题目描述
给你一个整数 n
,它表示一个 带权有向 图的节点数,节点编号为 0
到 n - 1
。
同时给你一个二维整数数组 edges
,其中 edges[i] = [fromi, toi, weighti]
,表示从 fromi
到 toi
有一条边权为 weighti
的 有向 边。
最后,给你三个 互不相同 的整数 src1
,src2
和 dest
,表示图中三个不同的点。
请你从图中选出一个 边权和最小 的子图,使得从 src1
和 src2
出发,在这个子图中,都 可以 到达 dest
。如果这样的子图不存在,请返回 -1
。
子图 中的点和边都应该属于原图的一部分。子图的边权和定义为它所包含的所有边的权值之和。
示例 1:
输入:n = 6, edges = [[0,2,2],[0,5,6],[1,0,3],[1,4,5],[2,1,1],[2,3,3],[2,3,4],[3,4,2],[4,5,1]], src1 = 0, src2 = 1, dest = 5 输出:9 解释: 上图为输入的图。 蓝色边为最优子图之一。 注意,子图 [[1,0,3],[0,5,6]] 也能得到最优解,但无法在满足所有限制的前提下,得到更优解。
示例 2:
输入:n = 3, edges = [[0,1,1],[2,1,1]], src1 = 0, src2 = 1, dest = 2 输出:-1 解释: 上图为输入的图。 可以看到,不存在从节点 1 到节点 2 的路径,所以不存在任何子图满足所有限制。
提示:
3 <= n <= 105
0 <= edges.length <= 105
edges[i].length == 3
0 <= fromi, toi, src1, src2, dest <= n - 1
fromi != toi
src1
,src2
和dest
两两不同。1 <= weight[i] <= 105
代码结果
运行时间: 541 ms, 内存: 80.4 MB
/*
* Problem: Find the minimum weight subgraph in a weighted directed graph such that
* both src1 and src2 can reach dest.
* Approach:
* 1. Use Dijkstra's algorithm with Java Streams to find the shortest paths.
* 2. Calculate the shortest path from src1 to dest, src2 to dest, and dest to any node.
* 3. Use Streams to iterate through nodes to find the minimum total distance.
* 4. If no valid path is found, return -1.
*/
import java.util.*;
import java.util.stream.*;
class Solution {
private static final int INF = Integer.MAX_VALUE;
public int minimumWeight(int n, int[][] edges, int src1, int src2, int dest) {
List<int[]>[] graph = IntStream.range(0, n).mapToObj(i -> new ArrayList<int[]>()).toArray(List[]::new);
List<int[]>[] reverseGraph = IntStream.range(0, n).mapToObj(i -> new ArrayList<int[]>()).toArray(List[]::new);
Arrays.stream(edges).forEach(edge -> {
graph[edge[0]].add(new int[]{edge[1], edge[2]});
reverseGraph[edge[1]].add(new int[]{edge[0], edge[2]});
});
long[] distFromSrc1 = dijkstra(graph, n, src1);
long[] distFromSrc2 = dijkstra(graph, n, src2);
long[] distToDest = dijkstra(reverseGraph, n, dest);
return IntStream.range(0, n)
.filter(i -> distFromSrc1[i] != INF && distFromSrc2[i] != INF && distToDest[i] != INF)
.mapToLong(i -> distFromSrc1[i] + distFromSrc2[i] + distToDest[i])
.min().orElse(INF) == INF ? -1 : (int) IntStream.range(0, n)
.filter(i -> distFromSrc1[i] != INF && distFromSrc2[i] != INF && distToDest[i] != INF)
.mapToLong(i -> distFromSrc1[i] + distFromSrc2[i] + distToDest[i])
.min().orElse(INF);
}
private long[] dijkstra(List<int[]>[] graph, int n, int start) {
long[] dist = new long[n];
Arrays.fill(dist, INF);
dist[start] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingLong(a -> a[1]));
pq.offer(new int[]{start, 0});
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int u = curr[0];
long d = curr[1];
if (d > dist[u]) continue;
graph[u].forEach(neighbor -> {
int v = neighbor[0];
long w = neighbor[1];
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.offer(new int[]{v, (int) dist[v]});
}
});
}
return dist;
}
}
解释
方法:
为了找到一个使得从两个源点 src1 和 src2 到目的点 dest 的最小带权子图,我们需要计算从 src1 和 src2 到所有其他点的最短路径,以及从所有点到 dest 的最短路径。由于路径是有向的,计算到 dest 的最短路径需要反转边的方向,然后从 dest 出发计算到所有点的最短路径。最后,对于图中的每一个节点 i,计算 src1 到 i,src2 到 i 和 i 到 dest 的路径之和,取这些和的最小值即为所求。如果这个最小值是无穷大,说明没有这样的路径存在。
时间复杂度:
O((E + V) log V)
空间复杂度:
O(E + V)
代码细节讲解
🦆
在使用Dijkstra算法计算从dest到所有节点的最短路径时,为什么需要反转图中的边?
▷🦆
您提到使用了三次Dijkstra算法,这是否意味着该方法在所有类型的图(如稠密图和稀疏图)中都是效率最优的?
▷🦆
在计算从src1, src2到所有点以及所有点到dest的最短路径后,如何确保得到的子图确实允许从src1和src2同时到达dest?
▷🦆
在寻找最小路径和时,代码中直接取了三个距离之和的最小值,这里是否考虑了所有必要的路径存在性验证?
▷