题解:洛谷 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
【核心思想】
-
问题分析:给定 \(N\) 种产奶量的奶牛,每种有 \(x_i\) 头,产奶量为 \(y_i\)。需要将所有奶牛两两配对,每对挤奶时间为两者产奶量之和,所有对同时挤奶。求完成挤奶的最短时间(即所有对中最大和的最小值)。这是一个双指针贪心问题,关键在于将产奶量排序后,用双指针将最小和最大的奶牛配对,使每对之和尽可能均衡。
-
算法选择:
- 排序 + 双指针:按产奶量升序排序,用双指针分别指向最小和最大的未配对奶牛
- 贪心配对:每次将当前最小产奶量的奶牛与当前最大产奶量的奶牛配对,更新答案为所有配对和的最大值
-
关键步骤:
- 读取输入:\(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序主导
- 空间复杂度:\(O(N)\),存储奶牛信息
-
双指针贪心配对的核心思想:
- 均衡化策略:将最小的与最大的配对,避免"大+大"导致极端大的和,使各对之和尽量接近
- 同数量处理:当某种产奶量的奶牛数量较多时,可能需要与多个其他产奶量的奶牛配对,通过剩余数量继续配对
- 并行时间瓶颈:所有对同时挤奶,总时间由最慢的一对决定,因此最小化最大配对和即为目标
- 最优性证明:该策略等价于排序后首尾配对,是使最大值最小化的经典贪心策略
- 适用于"排序后配对 + 最小化最大值"类问题,核心是通过双指针实现均衡配对
【解题思路】

【算法标签】
普及- #双指针
【代码详解】
#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
浙公网安备 33010602011771号