题解:洛谷 P5683 [CSP-J2019 江西] 道路拆除
洛谷 P5683 [CSP-J2019 江西] 道路拆除
题意
有一个 \(n\) 个点 \(m\) 条边的无向图,边权均为 \(1\)。
要求删除一些边,使得 \(1\leadsto s_1\) 的长度 \(\le t_1\),\(1\leadsto s_2\) 的长度 \(\le t_2\)。
无解输出 \(-1\)。
\(n,m\le3\times10^3\)。
解法
无解的情况很好判断,只要初始图中就无法满足条件即可,否则必然有解。
转化为保留最少的边。
显然最后剩余的边必然形成一颗树,保留了 \(1\leadsto s_1\) 和 \(1\leadsto s_2\) 的路径。
考虑两条路径最后一个相同点 \(i\),枚举 \(i\) 即可。
枚举点 \(i\),然后统计三条边的最短路即可。
由于是无向图,所以可以转化为端点到某一点的距离,那么就可以用 BFS 预处理了。
答案如下,但是要满足 \(t\) 的限制。
\[m-\min\limits_{i=1}^{n}\left\{w(1,i)+w(s_1,i)+w(s_2,i)\right\}
\]
时间复杂度
- 预处理距离,\(O(n+m)\)。
- 枚举每一个点并计算,\(O(n)\)。
时间复杂度 \(O(n+m)\),可以通过。
细节证明
"最后剩余的边必然形成一颗树"。
那么最后剩余的边会不会有环呢?
假设最后剩余的边有环,那么我们只需要保留其与树的交集。由于边权为 \(1\),所以经过边数越多权值越大。
这样就又变回了一颗树。
代码
/**
* Problem: P5683 [CSP-J2019 江西] 道路拆除
* Author: OIer_wst
* Date: 2025-06-30
*/
#include <bits/stdc++.h>
using lint = long long;
const int N = 3e3 + 10;
const int INF = 0x3f3f3f3f;
int hh, tt, q[N];
std::vector<int> g[N];
int n, m, ans = -1, s1, t1, s2, t2, dis[3][N];
void bfs(int x, int dis[]) {
memset(dis, 0x3f, N << 2);
hh = tt = 0, q[0] = x, dis[x] = 0;
while (hh <= tt) {
int u = q[hh++];
for (auto v : g[u]) {
if (dis[v] == INF) {
dis[v] = dis[u] + 1;
q[++tt] = v;
}
}
}
}
int main() {
std::cin >> n >> m;
for (int i = 0, u, v; i < m; ++i) {
std::cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
std::cin >> s1 >> t1 >> s2 >> t2;
bfs(1, dis[0]), bfs(s1, dis[1]), bfs(s2, dis[2]);
for (int i = 1; i <= n; ++i) {
if (dis[0][i] + dis[1][i] <= t1 && dis[0][i] + dis[2][i] <= t2) {
ans = std::max(ans, m - dis[0][i] - dis[1][i] - dis[2][i]);
}
}
std::cout << ans << std::endl;
return 0;
}
模型归纳
将路径拆分成几个端分别解决。
类似题目:
技巧
正难则反,将「最大化删除」转化为「最小化保留」,从而使题目的思考变得容易。

浙公网安备 33010602011771号