题解:洛谷 P9937 Acowdemia I
【题目来源】
洛谷:P9937 [USACO21OPEN] Acowdemia I B - 洛谷
【题目描述】
由于对计算机科学的热爱,以及有朝一日成为 「Bessie 博士」的诱惑,奶牛 Bessie 开始攻读计算机科学博士学位。经过一段时间的学术研究,她已经发表了 \(N\) 篇论文(\(1 \le N \le 10^5\)),并且她的第 \(i\) 篇论文得到了来自其他研究文献的 \(c_i\) 次引用(\(0 \le c_i \le 10^5\))。
Bessie 听说学术成就可以用 \(h\) 指数来衡量。\(h\) 指数等于使得研究员有至少 \(h\) 篇引用次数不少于 \(h\) 的论文的最大整数 \(h\)。例如,如果一名研究员有 \(4\) 篇论文,引用次数分别为 \((1,100,2,3)\),则 \(h\) 指数为 \(2\),然而若引用次数为 \((1,100,3,3)\) 则 \(h\) 指数将会是 \(3\)。
为了提升她的 \(h\) 指数,Bessie 计划写一篇综述,并引用一些她曾经写过的论文。由于页数限制,她至多可以在这篇综述中引用 \(L\) 篇论文(\(0 \le L \le 10^5\)),并且她只能引用每篇她的论文至多一次。
请帮助 Bessie 求出在写完这篇综述后她可以达到的最大 \(h\) 指数。
注意 Bessie 的导师可能会告知她纯粹为了提升 \(h\) 指数而写综述存在违反学术道德的嫌疑;我们不建议其他学者模仿 Bessie 的行为。
【输入】
输入的第一行包含 \(N\) 和 \(L\)。
第二行包含 \(N\) 个空格分隔的整数 \(c_1,\ldots,c_N\)。
【输出】
输出写完综述后 Bessie 可以达到的最大 \(h\) 指数。
【输入样例】
4 0
1 100 2 3
【输出样例】
2
【核心思想】
-
问题分析:给定 \(N\) 篇论文的引用次数 \(c_i\),综述可以额外给至多 \(L\) 篇论文各增加 1 次引用(每篇最多加 1 次)。求操作后最大的 \(h\) 指数,即满足“至少有 \(h\) 篇论文引用数 \(\ge h\)”的最大整数 \(h\)。由于单次加引用只能将 \(h-1\) 变成 \(h\),所以问题转化为判断给定 \(h\) 是否可达。
-
算法选择:
- 二分答案:\(h\) 的范围是 \([0, N]\),单调性成立:若 \(h\) 可达,则任意 \(h' < h\) 也可达,因此可对 \(h\) 进行二分。
- 贪心判定:对于固定的 \(h\),统计两类论文:
- \(cnt1\):引用数 \(\ge h\) 的论文数(已经满足条件)
- \(cnt2\):引用数 \(= h-1\) 的论文数(差 1 次引用即可满足)
若 \(cnt1 \ge h\),则直接满足;否则,需要用综述补足差额 \(need = h - cnt1\),条件为 \(need \le L\) 且 \(need \le cnt2\)。
-
关键步骤:
- 读入数据:读取 \(N\)、\(L\) 和 \(N\) 个引用数 \(c_i\)。
- 二分枚举 \(h\):左边界 \(l=0\),右边界 \(r=N\)(\(h\) 不可能超过论文总数)。当 \(l \le r\) 时:
- 计算 \(mid = (l+r)/2\)。
- 若 \(check(mid)\) 为真,说明 \(mid\) 可达,尝试更大的 \(h\):\(l = mid + 1\)。
- 否则,说明 \(mid\) 不可达,尝试更小的 \(h\):\(r = mid - 1\)。
- 判定函数 \(check(h)\):
- 初始化 \(cnt1=0\),\(cnt2=0\)。
- 遍历所有论文:
- 若 \(c_i \ge h\),\(cnt1++\);
- 若 \(c_i == h-1\),\(cnt2++\)。
- 若 \(cnt1 \ge h\),返回
true。 - 计算差额 \(need = h - cnt1\),返回 \(need \le L\) 且 \(need \le cnt2\)。
- 输出答案:最终 \(r\) 即为最大可达 \(h\) 指数。
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),二分次数 \(O(\log N)\),每次判定遍历 \(N\) 篇论文。
- 空间复杂度:\(O(N)\),存储引用次数。
-
二分答案 + 贪心判定的核心思想:
- 单调性利用:\(h\) 指数的可行性随 \(h\) 增加而单调递减,适合二分。
- 关键约束识别:综述只能给每篇论文增加 1 次引用,因此只有引用数为 \(h-1\) 的论文有潜力在加 1 次后达到 \(h\),引用数更低的论文即使加 1 次也仍小于 \(h\),无帮助。
- 贪心补给:为了达到目标 \(h\),应优先将综述引用分配给那些最接近条件的论文(即引用数为 \(h-1\) 的论文),这是最优且唯一有效的策略。
- 适用场景:适用于满足“可行性关于答案单调”且“判定过程可贪心”的优化问题,常见于有限的均匀增量分配场景。
【解题思路】

【算法标签】
普及- #整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, l, c[100005];
bool check (int h)
{
int cnt1 = 0, cnt2 = 0; // cnt1用来统计大于等于h的数量,cnt2用来统计与h差1个数量
for (int i=1; i<=n; i++) {
if (c[i]>=h) cnt1++;
if (c[i]==h-1) cnt2++;
}
if (cnt1>=h) return true; // 如果直接满足大于等于h,就返回true
if (h-cnt1<=l && h-cnt1<=cnt2) return true; // 如果加上l篇文章就够h篇,且加上cnt2也够h篇,也返回true
return false;
}
int main()
{
cin >> n >> l; // 输入n和l
for (int i=1; i<=n; i++) { // 输入n篇论文的引用次数
cin >> c[i];
}
sort(c+1, c+n+1); // 按照从小到大排序
int l = 0, r = n; // 进行二分搜索
while (l<=r) {
int mid = l + (r-l)/2;
if (check(mid)) l = mid + 1; // 如果不满足条件,l增加
else r = mid-1; // 否则r减少
}
cout << r << endl; // 输出r
return 0;
}
【运行结果】
4 0
1 100 2 3
2
浙公网安备 33010602011771号