【Floyd】AtCoder ABC375 F. Road Blocked
前言
Floyd 算法(弗洛伊德算法)是解决图论中全源最短路径问题的一种经典算法。全源最短路径问题是指对于给定的一个带权有向图(也可以是无向图,将无向图看作双向有向图即可),求出每一对顶点之间的最短路径长度。Floyd 算法是动态规划思想的一个典型应用,动态规划的过程是引入一个中转节点来松弛路径。
题目
https://atcoder.jp/contests/abc375/tasks/abc375_f
题意
第一行,输入三个正整数 \(n,m,q(2 \leq n\leq300,0 \leq m \leq \frac{n \times (n-1)}{2},1 \leq q \leq 2 \times 10^5)\),代表一张 \(n\) 个节点 \(m\) 条无向边的图和进行询问的次数;
接下来 \(m\) 行,每行输入三个正整数 \(x_i,y_i,w_i(1 \leq x_i < y_i \leq n, 1 \leq w_i \leq 10^9)\),代表节点 \(x_i\) 和 \(y_i\) 之间连接了一条长度为 \(w_i\) 的无向边;
最后输入 \(q\) 行,输入的类型有:
- 1 i:将输入的第 \(i\) 条边删除(永久生效)
- 2 x y:询问图中节点 \(x,y\) 之间的最短距离
对于每个类型 2 的询问,都输出最短距离。若无论如何无法抵达,则输出 \(-1\)。
保证:无向图中没有重边和自环;类型 1 的询问至多只有 min\((q, 300)\) 次。
题解
对于询问类型 2,本质是询问全源最短路径长度,Floyd 算法时间复杂度为\(O(n^3)\),满足该题时间复杂度要求,因此可以往 Floyd 算法的方向思考。
使用 Floyd 算法维护全源最短路径长度,初始化操作是将全部路径置为无穷大。对于操作类型 1,相当于重新将一条无向边置为无穷大。
但此时引入一个新的问题:若按照询问的顺序进行删边,由于所删边的节点可能是其他最短路径的中转接点,则可能影响到多条路径,故不得不重新跑一边 Floyd 算法。类型 1 的询问次数在 min(\(q,300\)) 次以内,时间复杂度为 \(O(n^4)\)。e8 级别的时间复杂度是比较危险的时间复杂度,如果不是实在别无选择,不建议交这种比较危险的复杂度的题解,容易 tle。
删除一条边,不得不重新将 Floyd 算法重跑一遍,单次操作时间复杂度为 \(O(n^3)\)。而添加一条边,相当于引入两个中转节点,Floyd 算法每次引入一个中转节点,单次操作时间复杂度为 \(O(n^2)\)。
反向执行一次类型 1 的操作,相当于更新一条无向边的长度,本质是更新了两个中转节点,执行 min(\(q, 300\)) 次的时间复杂度来到 \(O(n^3)\),也是满足要求的时间复杂度。
因此,可以反向考虑执行类型 1 的操作。具体的算法步骤如下:
- 将一张无向图的全部边均置为无穷大;
- 将全部从未删除的边初始化到无向图中;
- 执行 Floyd 算法,计算出全源最短路径长度;
- 反向执行询问操作,对于类型 2 的询问,直接输出最短路径长度;对于类型 1 的询问,尝试更新无向边的长度,若当前要更新的长度大于当前无向边长度,则放弃更新,否则更新,并以该无向边的两个节点作为中转节点进行更新全源最短路径长度。
时间复杂度:\(O(n^3)\)
空间复杂度:\(O(n^2)\)
参考代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr ll INF = 1e18;
int n, m, q;
ll a[301][301];
bool d[45007];
void floyd_(int k) {//引入节点 k 作为中转节点
for (int i = 1; i <= n; ++ i) {
if (i == k) continue;
for (int j = 1; j <= n; ++ j) {
a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
}
}
}
int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n >> m >> q;
vector<int> x(m + 1), y(m + 1), w(m + 1), t(q), u(q), v(q);
for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) a[i][j] = INF;
for (int i = 1; i <= m; ++ i) cin >> x[i] >> y[i] >> w[i];
for (int i = 0; i < q; ++ i) {
cin >> t[i] >> u[i];
if (t[i] == 2) cin >> v[i];
else d[u[i]] = true;
}
for (int i = 1; i <= m; ++ i) if (!d[i]) a[x[i]][y[i]] = a[y[i]][x[i]] = w[i];
for (int k = 1; k <= n; ++ k) floyd_(k);
stack<ll> stk;
for (int i = q - 1; i >= 0; -- i) {
if (t[i] == 2) stk.emplace(a[u[i]][v[i]] == INF ? -1 : a[u[i]][v[i]]);
else {
int &id = u[i];
bool flag = false;
if (a[x[id]][y[id]] > w[id]) {
a[x[id]][y[id]] = w[id];
flag = true;
}
if (a[y[id]][x[id]] > w[id]) {
a[y[id]][x[id]] = w[id];
flag = true;
}
if (flag) floyd_(x[id]), floyd_(y[id]);
}
}
while (!stk.empty()) {
cout << stk.top() << '\n';
stk.pop();
}
return 0;
}
浙公网安备 33010602011771号