P8906 [USACO22DEC] Breakdown P [最短路]
P8906 [USACO22DEC] Breakdown P
Solution
-
经典 trick,删边比较难处理,转换成加边,倒着处理。
-
那我们接下来要考虑,怎么记录状态,以及,每加一次边要如何更新状态。
-
还是比较套路地,我们可以求出
到某个点 经过 条边的最短路,再求出 到 经过 条边的最短路。根据这种思想,我们可能会思考折半搜索,分治之类的算法来解决这道题。 -
如果我们要求
,就要求 ,然后又要求 。注意数据范围 ,这种数据范围可能会成为解题的关键。那么, , , 。 显然就是边界,就等于两点间的边权。如何求 呢?是不是只需要合并两条边。求 只需要把 的答案合并。这样一来,我们要记录的信息就非常少,不需要去写分治之类的递归程序。 -
至于怎么合并。我们先考虑
到某一点 的情况, 到 同理。我们设 表示 到 经过 条边的最短路,设 表示 到 只经过两条边的最短路。为啥 只记录终点, 要记录起点和终点,因为 显然只有终点才是有用的, 的话要记录起点和终点才能合并给 ,不然咋合并? -
先大致口胡一遍算法。
- 如果
,那 。 - 如果
,那么 , 是边权。 - 如果
,那么 。 - 如果
,那么 。
- 如果
-
那我们接下来考虑如何更新状态,且是在正确的复杂度内。
-
先考虑如何更新
。假设当前加的是边 ,那么 这条路径可能被更新 ( 是任意点), 枚举一个 ,路径 也可能被更新,再 枚举一个 。更新 的总复杂度 。 -
考虑如何更新
,这里以 为例。-
假设
经过的是这样一条边 。 -
如果当前加的边是
,那么只需要 枚举一个 ,然后 即可。 -
如果当前加的边是
,只需要 枚举 ,然后 。 -
如果当前加的边是
, 枚举 ,然后也是 。(欸,怎么写起来都是一样的,不管了qwq) -
以上情况的总复杂度都是
。 -
那如果当前加入的是
呢?我们肯定要 枚举 和 ,但是注意到,起点为 的边只有 条,于是均摊复杂度只有 ,还是能过的。
-
-
综上,总复杂度
,于是就可以愉快地 coding 了。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 3e2 + 5, M = N * N, inf = 0x3f3f3f3f;
int n, m, k, w[N][N], ans[M];
void Min(int &a, int b) {return a = min(a, b), void();}
struct Edge {int u, v;} e[M];
struct Worker {
int f[N]; // st(起点) 到 i 的经过 k 条边的最短路
int g[N][N]; // i 到 j 经过 2 条边的最短路
int w[N][N]; // 边权
int st, k;
void init(int _st, int _k) {
st = _st, k = _k;
memset(f, 0x3f, sizeof(f));
memset(g, 0x3f, sizeof(g));
memset(w, 0x3f, sizeof(w));
}
void add(int u, int v, int _w) {
w[u][v] = _w;
if (k == 1) {
if (u == st) f[v] = w[u][v];
return;
}
for (int i = 1; i <= n; i++) {
Min(g[i][v], w[i][u] + w[u][v]);
Min(g[u][i], w[u][v] + w[v][i]);
}
if (k == 2) {
for (int i = 1; i <= n; i++) f[i] = g[st][i];
return;
}
if (k == 3) {
if (u == st) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
Min(f[i], g[u][j] + w[j][i]);
}
}
} else {
for (int i = 1; i <= n; i++) {
Min(f[i], w[st][u] + g[u][i]);
Min(f[i], w[st][v] + g[v][i]);
// Min(f[u], w[st][i] + g[i][u]); 该情况不会被更新
Min(f[v], w[st][i] + g[i][v]);
}
}
}
if (k == 4) {
if (u == st) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
Min(f[i], g[u][j] + g[j][i]);
}
}
} else {
for (int i = 1; i <= n; i++) {
Min(f[i], g[st][u] + g[u][i]);
Min(f[i], g[st][v] + g[v][i]);
// Min(f[u], g[st][i] + g[i][u]); 该情况不会被更新
Min(f[v], g[st][i] + g[i][v]);
}
}
}
}
}work1, workn;
int main() {
cin >> n >> k;
m = n * n;
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cin >> w[i][j];
for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v;
int k1 = k / 2, kn = k - k1;
work1.init(1, k1), workn.init(n, kn);
for (int i = m; i >= 1; i--) {
ans[i] = inf;
for (int j = 1; j <= n; j++) ans[i] = min(ans[i], work1.f[j] + workn.f[j]);
work1.add(e[i].u, e[i].v, w[e[i].u][e[i].v]);
workn.add(e[i].v, e[i].u, w[e[i].u][e[i].v]);
}
for (int i = 1; i <= m; i++) cout << (ans[i] == inf ? -1 : ans[i]) << "\n";
return 0;
}```
本文作者:chenwenmo
本文链接:https://www.cnblogs.com/chenwenmo/p/18535931
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步