UVa 10985 Rings n Ropes

题目描述

我们有 nnn 个钢制小环和 mmm 条长度完全相同的绳子。每条绳子的两端分别系在两个不同的环上。

现在,我们将用左手拿起一个环 LLL,用右手拿起另一个环 RRR,然后尽力将整个结构拉开。在这个过程中,一些绳子会被水平拉伸,而其他绳子则会下垂或变形。我们的目标是选择 LLLRRR,使得被水平拉伸的绳子数量尽可能多。

假设绳子的伸长可以忽略不计,所有绳子的厚度可以忽略不计,并且可以在它们所系的环上自由滑动。每个环的厚度和半径也可以忽略不计。

输入格式

第一行输入给出测试用例的数量 NNN。接下来是 NNN 个测试用例。每个测试用例以两行开始,分别给出 nnn2≤n≤1202 \leq n \leq 1202n120)和 mmm0≤m≤n(n−1)/20 \leq m \leq n(n-1)/20mn(n1)/2)。接下来的 mmm 行每行包含两个不同的环(整数范围在 [0,n−1][0, n-1][0,n1] 内)。每对环之间最多由一条绳子连接。

输出格式

对于每个测试用例,输出一行 Case #x:,后面跟着通过选择一对环 LLLRRR 所能水平拉伸的绳子的最大数量。

题目分析

问题理解

这个问题的核心在于理解什么时候一条绳子会被水平拉伸。当我们选择两个环 LLLRRR 并将它们拉开时:

  • 如果一条绳子的两个端点都在 LLL 侧,那么这条绳子不会被拉伸
  • 如果一条绳子的两个端点都在 RRR 侧,那么这条绳子也不会被拉伸
  • 只有当一条绳子的一个端点在 LLL 侧,另一个端点在 RRR 侧时,这条绳子才会被水平拉伸

关键洞察

将问题建模为图论问题:

  • 将环看作图中的顶点
  • 将绳子看作图中的边
  • 当选择 LLLRRR 时,所有连接 LLL 侧顶点和 RRR 侧顶点的边(绳子)都会被水平拉伸

那么如何确定哪些顶点在 LLL 侧,哪些在 RRR 侧呢?

实际上,当我们拉开 LLLRRR 时,整个结构会沿着 LLLRRR 的最短路径被拉开。对于任意顶点 vvv

  • 如果 dist(L,v)<dist(R,v)\texttt{dist}(L, v) < \texttt{dist}(R, v)dist(L,v)<dist(R,v),则 vvvLLL
  • 如果 dist(L,v)>dist(R,v)\texttt{dist}(L, v) > \texttt{dist}(R, v)dist(L,v)>dist(R,v),则 vvvRRR
  • 如果 dist(L,v)=dist(R,v)\texttt{dist}(L, v) = \texttt{dist}(R, v)dist(L,v)=dist(R,v),则 vvv 在中间位置

算法思路

  1. 计算所有点对最短路径:使用 Floyd-Warshall\texttt{Floyd-Warshall}Floyd-Warshall 算法计算图中所有顶点对之间的最短路径距离。

  2. 枚举所有可能的 (L,R)(L, R)(L,R):对于每一对不同的顶点 (L,R)(L, R)(L,R),统计会被水平拉伸的绳子数量。

  3. 判断绳子是否被拉伸:对于每条绳子 (a,b)(a, b)(a,b),它会被水平拉伸当且仅当:

    • aaabbb 都在 LLLRRR 的某条最短路径上
    • aaabbb 在最短路径上处于不同的"侧",即 dist[L][a]≠dist[L][b]\texttt{dist}[L][a] \neq \texttt{dist}[L][b]dist[L][a]=dist[L][b]
  4. 记录最大值:在所有 (L,R)(L, R)(L,R) 对中,找到能产生最多水平拉伸绳子数量的那一对。

复杂度分析

  • Floyd-Warshall\texttt{Floyd-Warshall}Floyd-Warshall 算法的时间复杂度为 O(n3)O(n^3)O(n3),其中 n≤120n \leq 120n120,所以 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(n2m)
  • 在最坏情况下 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 算法计算所有点对最短路径,然后利用最短路径的性质来判断哪些绳子会被水平拉伸。算法的时间复杂度虽然较高,但在题目给定的约束条件下是可接受的。

这种将实际问题建模为图论问题,然后利用经典图算法解决的思路在编程竞赛中非常常见,掌握这种思维方式对于解决类似问题很有帮助。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值