D - Shortest Path 3
题目大意:
给你一个无向图,图中有 n n n 个顶点和 m m m 条边。每个顶点 i ( 1 ≤ i ≤ n ) i(1 \leq i \leq n) i(1≤i≤n) 的权重为 a i a_{i} ai。每条边 j ( 1 ≤ j ≤ m ) j(1 \leq j \leq m) j(1≤j≤m) 双向连接顶点 u j u_{j} uj 和 v j v_{j} vj ,权重为 b j b_{j} bj。
该图中路径的权重定义为路径上出现的顶点和边的权重之和。
针对每个 i = 2 , 3 , … , n i = 2, 3, …, n i=2,3,…,n 求解以下问题:
求从顶点 1 1 1 到顶点 i i i 的路径的最小权重。
数据范围
- 2 ≤ n ≤ 2 × 1 0 5 2 \leq n \leq 2 \times 10^5 2≤n≤2×105
- n − 1 ≤ m ≤ 2 × 1 0 5 n-1 \leq m \leq 2 \times 10^5 n−1≤m≤2×105
- 1 ≤ u j < v j ≤ n 1 \leq u_j < v_j \leq n 1≤uj<vj≤n
- ( u i , v i ) ≠ ( u j , v j ) (u_i, v_i) \neq (u_j, v_j) (ui,vi)=(uj,vj) 如果 i ≠ j i \neq j i=j .
- 图是连通的。
- 0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0≤ai≤109
- 0 ≤ b j ≤ 1 0 9 0 \leq b_j \leq 10^9 0≤bj≤109
- 所有输入值均为整数。
分析:
很明显可以看出来,这是从起点到每个点的最短距离,则这一题可以使用 D i j k s t r a Dijkstra Dijkstra算法求解,这里 D i j k s t r a Dijkstra Dijkstra算法是每个人都必须要掌握的板子,我便不再赘述了。要注意的是,朴素版本的 D i j k s t r a Dijkstra Dijkstra算法时间复杂度为 O ( n 2 ) O(n^2) O(n2)的,其中 n n n为点的数量。这里因为数据范围较大,所以需要使用堆优化版的,即优先队列 D i j k s t r a Dijkstra Dijkstra,时间复杂度为 O ( m × l o g ( n ) ) O(m\times log(n)) O(m×log(n)),其中 m m m为边的数量, n n n为点数。
当你确定算法后,就可以发现,其实这就是一道很简单的板子题,唯一不同的是:在
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra算法中并不存在点权这一说。如下图:
设·从
1
1
1 号点到
5
5
5 号点的最短距离就是上图中的路线,其中
w
,
x
,
y
,
z
w, x, y, z
w,x,y,z为边权,
a
1
,
a
2
,
a
3
,
a
4
,
a
5
a_1, a_2, a_3, a_4, a_5
a1,a2,a3,a4,a5为点权。
纯板子
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra中,
d
i
s
t
5
′
=
w
+
x
+
y
+
z
dist'_5 = w + x + y + z
dist5′=w+x+y+z,但是本题目中,我们需要求得的应该为
d
i
s
t
5
=
a
1
+
a
2
+
a
3
+
a
4
+
a
5
+
w
+
x
+
y
+
z
dist_5 = a_1 + a_2 + a_3 + a_4 + a_5 + w + x + y + z
dist5=a1+a2+a3+a4+a5+w+x+y+z。由于
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra的特性,我们仅能知道该最短路的起点和终点,无法得出此最短路中经过了哪些点,要加点权的话,我们不妨可以先将点权集成到边权中。 如下图:
此时我们通过
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra计算出来的
d
i
s
t
5
dist_5
dist5为:
d
i
s
t
5
′
=
(
a
1
+
w
+
a
2
)
+
(
a
2
+
x
+
a
3
)
+
(
a
3
+
y
+
a
4
)
+
(
a
4
+
z
+
a
5
)
=
a
1
+
2
∗
a
2
+
2
∗
a
3
+
2
∗
a
4
+
a
5
+
w
+
x
+
y
+
z
\begin{aligned} dist'_5 &= (a_1 + w + a_2) + (a_2 + x + a_3) + (a_3 + y + a_4) + (a_4 +z + a_5) \\ &= a_1 + 2 * a_2 + 2 * a_3 + 2 * a_4 + a_5 + w + x + y + z \\ \end{aligned}
dist5′=(a1+w+a2)+(a2+x+a3)+(a3+y+a4)+(a4+z+a5)=a1+2∗a2+2∗a3+2∗a4+a5+w+x+y+z
观察上式,发现这与我们所求的
d
i
s
t
5
dist_5
dist5相比,中间每个点的点权我们都多加了一遍。上述加粗部分所说,由于我们不知道最短路的过程中会经过哪些点,所以我们无法将多余的部分删除,但是我们却是知道起点和终点,所以我们将起点和终点的点权再加一遍就会得到:
d
i
s
t
5
′
+
a
1
+
a
5
=
2
∗
a
1
+
2
∗
a
2
+
2
∗
a
3
+
2
∗
a
4
+
2
∗
a
5
+
w
+
x
+
y
+
z
dist'_5 + a_1 + a_5 = 2*a_1 + 2 * a_2 + 2 * a_3 + 2 * a_4 + 2 * a_5 + w + x + y + z
dist5′+a1+a5=2∗a1+2∗a2+2∗a3+2∗a4+2∗a5+w+x+y+z
可以发现,我们该式与我们所求的
d
i
s
t
5
dist_5
dist5相比,点权部分变成了两倍,边权部分正常,由于我们点权和边权都加到了一起,无法区分开来,那我们索性再加一遍边权,我们将一条边的边权值在改变一下:
此时我们纯
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra板子所求
d
i
s
t
5
′
dist'_5
dist5′为:
d
i
s
t
5
′
+
a
1
+
a
5
=
(
a
1
+
2
∗
w
+
a
2
)
+
(
a
2
+
2
∗
x
+
a
3
)
+
(
a
3
+
2
∗
y
+
a
4
)
+
(
a
4
+
2
∗
z
+
a
5
)
+
a
1
+
a
5
=
2
∗
a
1
+
2
∗
a
2
+
2
∗
a
3
+
2
∗
a
4
+
2
∗
a
5
+
2
∗
w
+
2
∗
x
+
2
∗
y
+
2
∗
z
=
2
∗
(
a
1
+
a
2
+
a
3
+
a
4
+
a
5
+
w
+
x
+
y
+
z
)
=
2
∗
d
i
s
t
5
\begin{aligned} dist'_5 + a_1 + a_5&= (a_1 + 2 * w + a_2 )+ (a_2 + 2 * x + a_3) + (a_3 + 2 * y + a_4) + (a_4 + 2 * z + a_5 )+ a _1 + a_5\\ &= 2 * a_1 + 2 * a_2 + 2 * a_3 + 2 * a_4 + 2 * a_5 + 2 * w + 2 * x + 2 * y + 2 * z \\ &= 2 * (a_1 + a_2 + a_3 + a_4 + a_5 + w + x + y + z) \\ &= 2 * dist_5 \end{aligned}
dist5′+a1+a5=(a1+2∗w+a2)+(a2+2∗x+a3)+(a3+2∗y+a4)+(a4+2∗z+a5)+a1+a5=2∗a1+2∗a2+2∗a3+2∗a4+2∗a5+2∗w+2∗x+2∗y+2∗z=2∗(a1+a2+a3+a4+a5+w+x+y+z)=2∗dist5
上式中
d
i
s
t
5
dist_5
dist5为该题所求。
AC代码:
#include <bits/stdc++.h>
#define _1 first
#define _2 second
using i64 = long long;
const int N = 1e6 + 5, M = 1e2 + 5;
const i64 INF = 1e18;
int n, m, k;
int h[N], cnt;
i64 a[N], dist[N];
struct Edge {
i64 v, w, next;
} g[N];
inline void add(int u, int v, i64 w) {
cnt ++;
g[cnt].w = w;
g[cnt].v = v;
g[cnt].next = h[u];
h[u] = cnt;
}
struct node {
i64 d, u;
bool operator<(const node& t)const {
return d > t.d;
} // 由于使用的优先队列priority_queue默认为大根堆,这里重载一下
};
inline void dijkstra() { // dijkstra纯板子
int s = 1;
for (int i = 1; i <= n; i ++) dist[i] = INF;
dist[s] = 0;
std::priority_queue<node> pq;
pq.push(node{0, s});
while (!pq.empty()) {
node tmp = pq.top(); pq.pop();
i64 u = tmp.u, d = tmp.d;
if (d != dist[u]) continue ;
for (int i = h[u]; i; i = g[i].next) {
i64 v = g[i].v, w = g[i].w;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push(node{dist[v], v});
}
}
}
return ;
}
void solve()
{
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) {
std::cin >> a[i];
}
for (int i = 1; i <= m; i ++) {
int x, y, z;
std::cin >> x >> y >> z;
add(x, y, z + z + a[x] + a[y]);
add(y, x, z + z + a[x] + a[y]);
// 无向图,定义边权 = 2倍原本边权 + 所连两点的点权
}
dijkstra();
for (int i = 2; i <= n; i ++) {
std::cout << (dist[i] + a[1] + a[i]) / 2 << ' ';
} std::cout << '\n';
}
int main()
{
std::ios::sync_with_stdio(false);std::cout.tie(nullptr);std::cout.tie(nullptr);
int _ = 1; // std::cin >> (_);
while (_ --) {
solve();
}
return 0;
}