题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(N\) 篇论文的引用次数 \(c_i\),综述可以额外给至多 \(L\) 篇论文各增加 1 次引用(每篇最多加 1 次)。求操作后最大的 \(h\) 指数,即满足“至少有 \(h\) 篇论文引用数 \(\ge h\)”的最大整数 \(h\)。由于单次加引用只能将 \(h-1\) 变成 \(h\),所以问题转化为判断给定 \(h\) 是否可达。

  2. 算法选择

    • 二分答案\(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\)
  3. 关键步骤

    • 读入数据:读取 \(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\) 指数。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),二分次数 \(O(\log N)\),每次判定遍历 \(N\) 篇论文。
    • 空间复杂度:\(O(N)\),存储引用次数。
  5. 二分答案 + 贪心判定的核心思想

    • 单调性利用\(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
posted @ 2026-08-29 09:41  团爸讲算法  阅读(4)  评论(0)    收藏  举报