LeetCode-6032 得到要求路径的最小带权子图

本章内容

本章为LeetCode-2203. 得到要求路径的最小带权子图的题解。

题目内容

给你一个整数 n ,它表示一个 带权有向图 的节点数,节点编号为 0n - 1

同时给你一个二维整数数组 edges ,其中 \(edges[i] = [from_i, to_i, weight_i]\) ,表示从 \(from_i\)\(to_i\) 有一条边权为 \(weight_i\)有向 边。

最后,给你三个 互不相同 的整数src1src2dest ,表示图中三个不同的点。

请你从图中选出一个 边权和最小 的子图,使得从 src1 和 src2 出发,在这个子图中,都 可以 到达 dest 。如果这样的子图不存在,请返回 -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]] 也能得到最优解,但无法在满足所有限制的前提下,得到更优解。

解题思路

做LeetCode比赛的时候遇到这题,刚看见的时候感觉应该不难,但因为没有时间就没能深入思考。

实际上本题不难,用的也是图论中比较经典的思想,枚举中间点,通过枚举中间点\(i\),计算src1到\(i\)的距离 + src2到\(i\)的距离 + \(i\)到dest的距离,求出最小的\(i\),上述的枚举包含所有的情况,因此直接计算即可。

求src1到\(i\)的距离可以使用Dijkstra算法,求\(i\)到dest的距离可以通过将边反向,求dest到各个点的距离来实现。

关于Dijkstra算法,本题如果不使用堆优化,会产生超时,因此顺带可以熟悉STL的使用。

STL使用 priority_queue 作为堆,默认是大根堆,因此为了得到最短路,可以将路径取相反数,放进大根堆。同时在Dijkstra算法中,用堆优化的时候,堆内的元素包含距离顶点,因此使用 pair 记录距离和顶点。将 pair 放进 priority_queue 时,默认的排序方式是先排第一个,再排第二个。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public:
#define MAXN 100000+20
typedef pair<long long, int> pp;
vector<vector<int>> v1, v2, w1, w2;
long long dis1[MAXN], dis2[MAXN], dis3[MAXN], now = -1;

void getDis(int o, int n, long long *dis, int *vis, vector<vector<int>>& v, vector<vector<int>>& w) {
for (int i = 0; i < n; i++) dis[i] = -1;
priority_queue<pp> q;
q.push(pp(0, o));
while (!q.empty()) {
pp p = q.top(); q.pop();
int sn = p.second;
long long val = -p.first;
if (dis[sn] >= 0) continue;
dis[sn] = val;
for (int i = 0; i < v[sn].size(); i++)
q.push(pp(-val - w[sn][i], v[sn][i]));
}
}

long long minimumWeight(int n, vector<vector<int>>& edges, int src1, int src2, int dest) {
int vis[MAXN];
v1 = v2 = w1 = w2 = vector<vector<int>>(n);

for (auto &edge : edges) {
v1[edge[0]].push_back(edge[1]); w1[edge[0]].push_back(edge[2]);
v2[edge[1]].push_back(edge[0]); w2[edge[1]].push_back(edge[2]);
}

getDis(src1, n, dis1, vis, v1, w1);
getDis(src2, n, dis2, vis, v1, w1);
getDis(dest, n, dis3, vis, v2, w2);

for (int i = 0; i < n; i++) {
if (dis1[i] == -1 || dis2[i] == -1 || dis3[i] == -1) continue;
if (now == -1) now = dis1[i] + dis2[i] + dis3[i];
if (dis1[i] + dis2[i] + dis3[i] < now)
now = dis1[i] + dis2[i] + dis3[i];
}
return now;
}
};