题解:洛谷 P4379 Lemonade Line
【题目来源】
洛谷:P4379 [USACO18OPEN] Lemonade Line S - 洛谷
【题目描述】
这是农场上一个炎热的夏日,Farmer John 要给他的 \(N\) 头奶牛发柠檬汽水了!所有的 \(N\) 头奶牛(编号为 \(1 \dots N\))都喜欢柠檬汽水,只是有些喜欢的程度更高一些。具体来说,奶牛 \(i\) 为了获得柠檬汽水,最多愿意排在 \(w_i\) 头奶牛之后。现在所有的 \(N\) 头奶牛都在田里,但只要 Farmer John 敲响牛铃,这些奶牛就会立刻赶到柠檬汽水站。她们会在 Farmer John 开始分发柠檬汽水之前到达,但没有两头奶牛会在同一时刻到达。此外,当奶牛 \(i\) 到达时,当且仅当队伍中至多有 \(w_i\) 头奶牛时,她才会加入队伍。
Farmer John 想要提前准备一定量的柠檬汽水,但他不想浪费。排队的奶牛数量可能取决于她们的到达顺序。请帮助他求出在所有可能的到达顺序下,最小的可能排队奶牛数量。
【输入】
第一行包含 \(N\),第二行包含 \(N\) 个用空格分隔的整数 \(w_1, w_2, \dots, w_N\)。输入保证 \(1 \leq N \leq 10^5\),此外对于每头奶牛 \(i\),\(0 \leq w_i \leq 10^9\)。
【输出】
输出在所有可能的奶牛到达顺序下,最小的可能排队奶牛数量。
【输入样例】
5
7 1 400 2 2
【输出样例】
3
【核心思想】
-
问题分析:给定 \(N\) 头奶牛,每头奶牛 \(i\) 最多愿意排在 \(w_i\) 头奶牛之后。奶牛按到达顺序依次决定是否入队,当且仅当当前队伍中已有奶牛数量 \(\leq w_i\) 时才会加入。目标是安排到达顺序,使得最终排队奶牛数量最小。
-
算法选择:
- 贪心策略:将奶牛按 \(w_i\) 从大到小排序,让"容忍度最高"的奶牛先到达
- 关键观察:若当前队伍已有 \(k\) 头奶牛,新到达的奶牛 \(i\) 愿意入队的条件是 \(w_i \geq k\)。为了让尽可能少的奶牛入队,应该让 \(w_i\) 大的奶牛优先到达,因为她们的容忍度高,更容易在队伍较长时仍愿意加入
-
关键步骤:
- 排序:将所有奶牛的 \(w_i\) 按降序排列
- 贪心遍历:按排序后的顺序模拟奶牛到达,维护当前队伍中已有的奶牛数量 \(cnt\)(初始为 \(0\))
- 入队判定:对于第 \(i\) 个到达的奶牛(排序后),若 \(w_i \geq cnt\)(即她愿意排在至少 \(cnt\) 头奶牛之后),则她加入队伍,\(cnt++\)
- 终止条件:若某头奶牛 \(w_i < cnt\),则她及之后所有奶牛都无法入队(因为后续奶牛的 \(w\) 更小或相等,队伍只会更长),直接终止遍历
- 输出答案:\(cnt\) 即为最小排队数量
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序 \(O(N \log N)\),遍历 \(O(N)\)
- 空间复杂度:\(O(N)\),存储 \(w\) 数组
-
贪心排序的核心思想:
- 优先让"宽容者"先到达:\(w_i\) 大的奶牛对队伍长度容忍度高,先让她们到达可以"占据"队伍位置,阻止 \(w_i\) 小的奶牛后续入队
- 反证法验证:假设存在最优解中某头 \(w\) 大的奶牛排在 \(w\) 小的奶牛之后,交换两者顺序后,\(w\) 大的奶牛仍然能入队(因为前面队伍更短),而 \(w\) 小的奶牛可能因前面变长而无法入队,结果不会变差
- 单调性利用:排序后,若第 \(i\) 个奶牛无法入队,则后续所有奶牛的 \(w\) 更小,必然也无法入队,可直接终止
- 问题转化:本质上是在寻找一个最长前缀,使得排序后第 \(i\) 个元素满足 \(w_i \geq i-1\)
- 适用于"按某种属性排序后贪心选择"的排队、调度类问题
【解题思路】

【算法标签】
普及- #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, w[100005], ans=0;
bool cmp(int x, int y)
{
return x>y;
}
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 输入n头奶牛的wi
cin >> w[i];
}
sort(w+1, w+n+1, cmp); // 按照从大到小方式排序
for (int i=1; i<=n; i++) { // 遍历n头奶牛
if (w[i]>=i-1) ans++; // 如果wi大于等于i-1,即大于等于队伍中的奶牛数,则说明这头奶牛可以进入队伍中
else break; // 否则结束循环(后面都肯定进不了队伍了)
}
cout << ans << endl; // 输出结果
return 0;
}
【运行结果】
5
7 1 400 2 2
3
浙公网安备 33010602011771号