题目大意:给定一张 n n 个点, 条边的加权有向图,有 q q 次询问,每次询问从点 出发经过至少 k k 条边走到点 的最短路是多少。 (n≤50,m≤10000,q≤100000) ( n ≤ 50 , m ≤ 10000 , q ≤ 100000 )
首先,我们可以想到倍增。
令
sp[i][j]
s
p
[
i
]
[
j
]
表示
i
i
点到 点的最短路。
dp0[k][i][j]
d
p
0
[
k
]
[
i
]
[
j
]
表示从
i
i
点出发经过恰好 条边走到
j
j
点的最短路。
表示从
i
i
点出发经过至少 条边走到
j
j
点的最短路。
状态转移方程如下:
计算
dp
d
p
值的时间复杂度为
Θ(log2m×n3)
Θ
(
log
2
m
×
n
3
)
。
对于每次询问,我们使用倍增的方法,可以在
Θ(q×log2m×n3)
Θ
(
q
×
log
2
m
×
n
3
)
时间内求出答案。
我们发现这样预处理的复杂度十分优秀,但是询问的复杂度会过高,会 TLE T L E 。而询问复杂度高的原因是复杂度中的 n3 n 3 。我们思考如何将 n3 n 3 优化到 n2 n 2 ,甚至 n n 。这是,我们就不能用类似计算 的方法计算答案了,而是要快速的找到一个中介点,然后用已有的 dp d p 值更新答案。
于是,我们考虑分块。
令
d[i][j]
d
[
i
]
[
j
]
表示
i
i
点到 点的边的长度,
M=m‾‾√
M
=
m
。
dp0[k][i][j]
d
p
0
[
k
]
[
i
]
[
j
]
表示从
i
i
点出发经过恰好 条边走到
j
j
点的最短路 。
dp1[k][i][j]
d
p
1
[
k
]
[
i
]
[
j
]
表示从
i
i
点出发经过恰好 条边走到
j
j
点的最短路 。
dp2[k][i][j]
d
p
2
[
k
]
[
i
]
[
j
]
表示从
i
i
点出发经过至少 条边走到
j
j
点的最短路 。
状态转移方程如下:
这样,预处理的时间复杂度是 Θ(Mn3) Θ ( M n 3 )
对于每个询问,我们枚举中间点 i i ,最小的 即是答案。时间复杂度 Θ(q×n) Θ ( q × n ) 。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 55;
int T, n, m, q;
int d[maxn][maxn];
int dp[105][maxn][maxn];
int dp1[105][maxn][maxn];
int dp2[105][maxn][maxn];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &m);
memset(d, 0x3f, sizeof(d));
for (int u, v, w, i = 1; i <= m; i++) {
scanf("%d %d %d", &u, &v, &w);
d[u][v] = min(d[u][v], w);
}
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) {
dp[0][i][i] = 0;
}
for (int k = 1; k <= 100; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int l = 1; l <= n; l++) {
dp[k][i][j] = min(dp[k][i][j], dp[k - 1][i][l] + d[l][j]);
}
}
}
}
memset(dp1, 0x3f, sizeof(dp1));
for (int i = 1; i <= n; i++) {
dp1[0][i][i] = 0;
}
for (int k = 1; k <= 100; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int l = 1; l <= n; l++) {
dp1[k][i][j] = min(dp1[k][i][j], dp1[k - 1][i][l] + dp[100][l][j]);
}
}
}
}
for (int i = 1; i <= n; i++) {
d[i][i] = 0;
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
memset(dp2, 0x3f, sizeof(dp2));
for (int k = 0; k <= 100; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int l = 1; l <= n; l++) {
dp2[k][i][j] = min(dp2[k][i][j], dp[k][i][l] + d[l][j]);
}
}
}
}
scanf("%d", &q);
int u, v, k;
while (q--) {
scanf("%d %d %d", &u, &v, &k);
int id1 = k / 100, id2 = k % 100;
int ans = 0x3f3f3f3f;
for (int i = 1; i <= n; i++) {
ans = min(ans, dp1[id1][u][i] + dp2[id2][i][v]);
}
printf("%d\n", ans == 0x3f3f3f3f ? -1 : ans);
}
}
return 0;
}