题解:洛谷 P3669 Paired Up

【题目来源】

洛谷:P3669 [USACO17OPEN] Paired Up S - 洛谷

【题目描述】

Farmer John 发现,当他的奶牛附近有另一头奶牛提供精神支持时,每头奶牛挤奶会更容易。因此,他希望将他的 \(M\) 头奶牛(\(M \leq 1,000,000,000\)\(M\) 为偶数)分成 \(M/2\) 对。每对奶牛将被引导到谷仓中一个单独的隔间进行挤奶。这些 \(M/2\) 个隔间中的挤奶过程将同时进行。

为了增加一些复杂性,Farmer John 的每头奶牛都有不同的产奶量。如果产奶量分别为 \(A\)\(B\) 的两头奶牛被配对,那么挤完它们总共需要 \(A+B\) 单位时间。

请帮助 Farmer John 确定整个挤奶过程完成所需的最少时间,假设他以最佳方式配对奶牛。

【输入】

输入的第一行包含 \(N\)\(1 \leq N \leq 100,000\))。

接下来的 \(N\) 行每行包含两个整数 \(x\)\(y\),表示 Farmer John 有 \(x\) 头产奶量为 \(y\) 的奶牛(\(1 \leq y \leq 1,000,000,000\))。所有 \(x\) 的总和为 \(M\),即奶牛的总数。

【输出】

输出 Farmer John 的奶牛挤奶所需的最少时间,假设它们以最佳方式配对。

【输入样例】

3
1 8
2 5
1 2

【输出样例】

10

【核心思想】

  1. 问题分析:给定 \(N\) 种产奶量的奶牛,每种有 \(x_i\) 头,产奶量为 \(y_i\)。需要将所有奶牛两两配对,每对挤奶时间为两者产奶量之和,所有对同时挤奶。求完成挤奶的最短时间(即所有对中最大和的最小值)。这是一个双指针贪心问题,关键在于将产奶量排序后,用双指针将最小和最大的奶牛配对,使每对之和尽可能均衡。

  2. 算法选择

    • 排序 + 双指针:按产奶量升序排序,用双指针分别指向最小和最大的未配对奶牛
    • 贪心配对:每次将当前最小产奶量的奶牛与当前最大产奶量的奶牛配对,更新答案为所有配对和的最大值
  3. 关键步骤

    • 读取输入\(N\)\(N\)\((x_i, y_i)\)
    • 排序:按 \(y_i\) 升序排列
    • 双指针配对\(i = 1\)\(j = N\)\(i \le j\)):
      • \(ans = \max(ans,\ y_i + y_j)\):更新最大配对和
      • \(cnt_i > cnt_j\)\(cnt_i \mathrel{-}= cnt_j\)\(j \mathrel{-}= 1\)(大头牛用完,移动 \(j\)
      • \(cnt_i < cnt_j\)\(cnt_j \mathrel{-}= cnt_i\)\(i \mathrel{+}= 1\)(小头牛用完,移动 \(i\)
      • \(cnt_i = cnt_j\)\(i \mathrel{+}= 1\)\(j \mathrel{-}= 1\)(两者同时用完)
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),排序主导
    • 空间复杂度:\(O(N)\),存储奶牛信息
  5. 双指针贪心配对的核心思想

    • 均衡化策略:将最小的与最大的配对,避免"大+大"导致极端大的和,使各对之和尽量接近
    • 同数量处理:当某种产奶量的奶牛数量较多时,可能需要与多个其他产奶量的奶牛配对,通过剩余数量继续配对
    • 并行时间瓶颈:所有对同时挤奶,总时间由最慢的一对决定,因此最小化最大配对和即为目标
    • 最优性证明:该策略等价于排序后首尾配对,是使最大值最小化的经典贪心策略
    • 适用于"排序后配对 + 最小化最大值"类问题,核心是通过双指针实现均衡配对

【解题思路】

【算法标签】

普及- #双指针

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n;
long long ans=-1e9;
struct node {
    int cnt, v;
}p[100005];
bool cmp (node a, node b)
{
    return a.v < b.v;
}
int main()
{
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) {  // 遍历n组数据
        cin >> p[i].cnt >> p[i].v;  // 记录每组的奶牛数和奶牛产奶量
    }
    sort(p+1, p+n+1, cmp);  // 按照产奶量从小到大排序
    int i=1, j=n;  // 定义双指针,分别指向头和尾
    while (i<=j) {  // 双指针模板
        ans = max(ans, 1ll*(p[i].v+p[j].v));  // 计算最大值(所有奶牛同时产奶,最短时间就是总和的最大值)
        if (p[i].cnt>p[j].cnt) {  // 如果i的数量大于j的数量
            p[i].cnt = p[i].cnt - p[j].cnt;  // 得到i的剩余数量,用于下次配对
            p[j].cnt = 0;  // j的数量修改为0(其实不改也行)
            j--;  // j向前移动
        } else if (p[i].cnt<p[j].cnt) {  // 如果j的数量大于i的数量
            p[j].cnt = p[j].cnt - p[i].cnt;  // 得到j的剩余数量,用于下次配对
            p[i].cnt = 0;  // i的数量修改为0(其实不改也行)
            i++;  // i向后移动
        } else {  // 否则(两者相等)
            i++;  // i向后移动
            j--;  // j向前移动
        }
    }
    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

3
1 8
2 5
1 2
10
posted @ 2026-08-29 09:37  团爸讲算法  阅读(3)  评论(0)    收藏  举报