题目描述
我们有 nnn 个钢制小环和 mmm 条长度完全相同的绳子。每条绳子的两端分别系在两个不同的环上。
现在,我们将用左手拿起一个环 LLL,用右手拿起另一个环 RRR,然后尽力将整个结构拉开。在这个过程中,一些绳子会被水平拉伸,而其他绳子则会下垂或变形。我们的目标是选择 LLL 和 RRR,使得被水平拉伸的绳子数量尽可能多。
假设绳子的伸长可以忽略不计,所有绳子的厚度可以忽略不计,并且可以在它们所系的环上自由滑动。每个环的厚度和半径也可以忽略不计。
输入格式
第一行输入给出测试用例的数量 NNN。接下来是 NNN 个测试用例。每个测试用例以两行开始,分别给出 nnn(2≤n≤1202 \leq n \leq 1202≤n≤120)和 mmm(0≤m≤n(n−1)/20 \leq m \leq n(n-1)/20≤m≤n(n−1)/2)。接下来的 mmm 行每行包含两个不同的环(整数范围在 [0,n−1][0, n-1][0,n−1] 内)。每对环之间最多由一条绳子连接。
输出格式
对于每个测试用例,输出一行 Case #x:,后面跟着通过选择一对环 LLL 和 RRR 所能水平拉伸的绳子的最大数量。
题目分析
问题理解
这个问题的核心在于理解什么时候一条绳子会被水平拉伸。当我们选择两个环 LLL 和 RRR 并将它们拉开时:
- 如果一条绳子的两个端点都在 LLL 侧,那么这条绳子不会被拉伸
- 如果一条绳子的两个端点都在 RRR 侧,那么这条绳子也不会被拉伸
- 只有当一条绳子的一个端点在 LLL 侧,另一个端点在 RRR 侧时,这条绳子才会被水平拉伸
关键洞察
将问题建模为图论问题:
- 将环看作图中的顶点
- 将绳子看作图中的边
- 当选择 LLL 和 RRR 时,所有连接 LLL 侧顶点和 RRR 侧顶点的边(绳子)都会被水平拉伸
那么如何确定哪些顶点在 LLL 侧,哪些在 RRR 侧呢?
实际上,当我们拉开 LLL 和 RRR 时,整个结构会沿着 LLL 到 RRR 的最短路径被拉开。对于任意顶点 vvv:
- 如果 dist(L,v)<dist(R,v)\texttt{dist}(L, v) < \texttt{dist}(R, v)dist(L,v)<dist(R,v),则 vvv 在 LLL 侧
- 如果 dist(L,v)>dist(R,v)\texttt{dist}(L, v) > \texttt{dist}(R, v)dist(L,v)>dist(R,v),则 vvv 在 RRR 侧
- 如果 dist(L,v)=dist(R,v)\texttt{dist}(L, v) = \texttt{dist}(R, v)dist(L,v)=dist(R,v),则 vvv 在中间位置
算法思路
-
计算所有点对最短路径:使用 Floyd-Warshall\texttt{Floyd-Warshall}Floyd-Warshall 算法计算图中所有顶点对之间的最短路径距离。
-
枚举所有可能的 (L,R)(L, R)(L,R) 对:对于每一对不同的顶点 (L,R)(L, R)(L,R),统计会被水平拉伸的绳子数量。
-
判断绳子是否被拉伸:对于每条绳子 (a,b)(a, b)(a,b),它会被水平拉伸当且仅当:
- aaa 和 bbb 都在 LLL 到 RRR 的某条最短路径上
- aaa 和 bbb 在最短路径上处于不同的"侧",即 dist[L][a]≠dist[L][b]\texttt{dist}[L][a] \neq \texttt{dist}[L][b]dist[L][a]=dist[L][b]
-
记录最大值:在所有 (L,R)(L, R)(L,R) 对中,找到能产生最多水平拉伸绳子数量的那一对。
复杂度分析
- Floyd-Warshall\texttt{Floyd-Warshall}Floyd-Warshall 算法的时间复杂度为 O(n3)O(n^3)O(n3),其中 n≤120n \leq 120n≤120,所以 1203=1,728,000120^3 = 1,728,0001203=1,728,000 是可接受的。
- 枚举所有 (L,R)(L, R)(L,R) 对的时间复杂度为 O(n2)O(n^2)O(n2),对于每个 (L,R)(L, R)(L,R) 对需要检查 mmm 条绳子,总复杂度为 O(n2⋅m)O(n^2 \cdot m)O(n2⋅m)。
- 在最坏情况下 m=O(n2)m = O(n^2)m=O(n2),所以总复杂度为 O(n4)O(n^4)O(n4),对于 n=120n = 120n=120 来说可能有点高,但在实际测试中是可接受的。
解题代码
// Rings n Ropes
// UVa ID: 10985
// Verdict: Accepted
// Submission Date: 2025-11-09
// UVa Run Time: 0.590s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int INF = 1000000000;
int main() {
int numCases;
cin >> numCases;
for (int caseIdx = 1; caseIdx <= numCases; ++caseIdx) {
int n, m;
cin >> n >> m;
vector<vector<int>> dist(n, vector<int>(n, INF));
for (int i = 0; i < n; ++i) {
dist[i][i] = 0;
}
vector<pair<int, int>> ropes;
for (int i = 0; i < m; ++i) {
int a, b;
cin >> a >> b;
dist[a][b] = dist[b][a] = 1;
ropes.push_back({a, b});
}
// Floyd-Warshall: 计算所有点对最短路径
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (dist[i][k] < INF && dist[k][j] < INF) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
int maxStretched = 0;
// 遍历所有可能的 L 和 R
for (int l = 0; l < n; ++l) {
for (int r = l + 1; r < n; ++r) {
if (dist[l][r] == INF) continue;
int countStretched = 0;
for (const auto& rope : ropes) {
int a = rope.first;
int b = rope.second;
// 检查a和b是否都在L到R的某条最短路径上
// 并且它们处于不同的"侧"
if (dist[l][a] + dist[a][r] == dist[l][r] &&
dist[l][b] + dist[b][r] == dist[l][r]) {
// a和b都在最短路径上,检查是否在不同侧
if (dist[l][a] != dist[l][b]) {
countStretched++;
}
}
}
maxStretched = max(maxStretched, countStretched);
}
}
cout << "Case #" << caseIdx << ": " << maxStretched << endl;
}
return 0;
}
总结
本题的关键在于将物理直觉转化为图论模型。通过 Floyd-Warshall\texttt{Floyd-Warshall}Floyd-Warshall 算法计算所有点对最短路径,然后利用最短路径的性质来判断哪些绳子会被水平拉伸。算法的时间复杂度虽然较高,但在题目给定的约束条件下是可接受的。
这种将实际问题建模为图论问题,然后利用经典图算法解决的思路在编程竞赛中非常常见,掌握这种思维方式对于解决类似问题很有帮助。
804

被折叠的 条评论
为什么被折叠?



