题解:洛谷 P8897 Cow College
【题目来源】
洛谷:P8897 [USACO22DEC] Cow College B - 洛谷
【题目描述】
Farmer John 计划为奶牛们新开办一所大学!

有 \(N(1 \le N \le 10^5)\) 头奶牛可能会入学。每头奶牛最多愿意支付 \(c_i\) 的学费 \((1 \le c_i \le 10^6)\)。Farmer John 可以设定所有奶牛入学需要支付的学费。如果这笔学费大于一头奶牛愿意支付的最高金额,那么这头奶牛就不会入学。Farmer John 想赚尽可能多的钱,从而可以给他的讲师提供一笔可观的工资。请求出他能赚到的钱的数量,以及此时应当收取多少学费。
【输入】
输入的第一行包含 \(N\)。
第二行包含 \(N\) 个整数 \(c_1,c_2, \cdots,c_N\),其中 \(c_i\) 是奶牛 \(i\) 愿意支付的最高学费金额。
【输出】
输出 Farmer John 可以赚到的最大金额以及最优情况下他应该收取的学费。如果有多个解,输出收取学费最小的解。
注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,Java 中的 "long",C/C++ 中的 "long long")。
【输入样例】
4
1 6 4 6
【输出样例】
12 4
【核心思想】
-
问题分析:给定 \(N\) 头奶牛愿意支付的最高学费 \(c_1, c_2, \dots, c_N\),Farmer John 需设定统一学费 \(p\)。愿意支付 \(\geq p\) 的奶牛会入学,总收入为 \(p \times \text{入学人数}\)。求最大收入及对应的最小最优学费。本质是一个排序后枚举定价的贪心问题。
-
算法选择:
- 排序 + 枚举定价:将奶牛按愿意支付金额排序,枚举每个可能的定价点,计算对应收入,取最大值
-
关键步骤:
- 排序:将 \(c[1..N]\) 按升序(或降序)排列
- 枚举定价(以升序为例,\(i\) 从 \(1\) 到 \(N\)):
- 若定价为 \(c[i]\),则所有愿意支付 \(\geq c[i]\) 的奶牛入学,人数为 \(N - i + 1\)
- 收入:\(\text{total} = c[i] \times (N - i + 1)\)
- 擂台法更新:若 \(\text{total} > \text{ans}\),更新 \(\text{ans} = \text{total}\),\(\text{ansx} = c[i]\)
- 输出答案:\(\text{ans}\) 和 \(\text{ansx}\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序 \(O(N \log N)\),枚举 \(O(N)\)
- 空间复杂度:\(O(N)\),存储学费数组
-
排序后枚举定价的核心思想:
- 离散最优定价点:最优定价一定等于某头奶牛愿意支付的金额(定价在 \(c[i]\) 和 \(c[i+1]\) 之间时,入学人数相同但收入更低)
- 单调性利用:排序后,定价越高入学人数越少,但单人支付更多;定价越低入学人数越多,但单人支付更少。收入是单峰函数,枚举所有离散点即可找到峰值
- 最小最优学费处理:使用严格大于
>更新最大值,当收入相同时不更新,保证保留最先遇到的最小定价;或使用>=配合降序排序实现同样效果 - 64位整数注意:\(N \leq 10^5\),\(c_i \leq 10^6\),收入最大可达 \(10^{11}\),需用
long long - 适用于定价策略、收益最大化、离散最优枚举类问题
【解题思路】

【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[100005], mark;
long long ans=1;
bool cmp(int x, int y) // 比较函数,按照从大到小排列
{
return x > y;
}
int main()
{
cin >> n; // 输入n
int tmp; // 定义临时变量,用来与最大值进行比较
for (int i=1; i<=n; i++) { // 遍历n头奶牛
cin >> a[i]; // 记录每个奶牛可支付的学费
}
sort(a+1, a+n+1, cmp); // 按照从大到小排列
for (long long i=1; i<=n; i++) { // i必须使用long long类型,否则有8个测试点会错误
long long tmp = i*a[i]; // 定义临时变量,记录这个人及之前可赚的学费
if (tmp >= ans) { // 擂台法,使用大于等于记录同一答案中,学费最小的解。
ans = tmp; // 记录最大值
mark = a[i]; // 记录最大值时最小的学费
}
}
cout << ans << " " << mark << endl; // 输出结果
return 0;
}
// 学而思课堂笔记
#include<bits/stdc++.h>
using namespace std;
int n, c[100005], ansx; // 总金额最高时,学费的最小值
long long total, ans; //
int main()
{
cin >> n;
for (int i=1; i<=n; i++) cin >> c[i];
sort(c+1, c+n+1);
for (int i=1; i<=n; i++) {
total = 1ll*c[i]*(n-i+1);
if (total > ans) {
ans = total;
ansx = c[i];
}
}
cout << ans << " " << ansx << endl;
return 0;
}
【运行结果】
4
1 6 4 6
12 4
浙公网安备 33010602011771号