3745. 牛的学术圈 I
题目描述
由于对计算机科学的热爱,以及有朝一日成为「Bessie 博士」的诱惑,奶牛 Bessie 开始攻读计算机科学博士学位。
经过一段时间的学术研究,她已经发表了 N 篇论文,并且她的第 i 篇论文得到了来自其他研究文献的 ci 次引用。
Bessie 听说学术成就可以用 h 指数来衡量。
h 指数等于使得研究员有至少 h 篇引用次数不少于 h 的论文的最大整数 h。
例如,如果一名研究员有 4 篇论文,引用次数分别为 (1, 100, 2, 3),则 h 指数为 2;然而若引用次数为 (1, 100, 3, 3) 则 h 指数将会是 3。
为了提升她的 h 指数,Bessie 计划写一篇综述,并引用一些她曾经写过的论文。
由于页数限制,她至多可以在这篇综述中引用 L 篇论文,并且她只能引用每篇她的论文至多一次。
请帮助 Bessie 求出在写完这篇综述后她可以达到的最大 h 指数。
注意:Bessie 的导师可能会告知她纯粹为了提升 h 指数而写综述存在违反学术道德的嫌疑;我们不建议其他学者模仿 Bessie 的行为。
输入格式
- 输入的第一行包含 N 和 L。
- 第二行包含 N 个空格分隔的整数 c1,…,cN。
输出格式
- 输出写完综述后 Bessie 可以达到的最大 h 指数。
数据范围
- 1 ≤ N ≤ 105
- 0 ≤ ci ≤ 105
- 0 ≤ L ≤ 105
输入样例1:
4 0
1 100 2 3
输出样例1:
2
样例1解释:
Bessie 不能引用任何她曾经写过的论文。上文中提到,(1, 100, 2, 3) 的 h 指数为 2。
输入样例2:
4 1
1 100 2 3
输出样例2:
3
样例2解释:
如果 Bessie 引用她的第三篇论文,引用数会变为 (1, 100, 3, 3)。上文中提到,这一引用数的 h 指数为 3。
h指数的解释与计算
h指数(h-index)是一种用来衡量一个研究员学术影响力的指标,特别是用来量化其科研成果的引用情况。计算h指数的步骤相对简单直观,以下是其基本计算流程:
- 收集论文及引用数:首先,收集该研究员所有学术论文的引用次数。
- 引用数排序:将这些论文按照引用次数从高到低进行排序。
- 确定h指数:从列表的顶端开始,向下检查,直到找到一个位置
N
,使得排在这个位置的论文的引用次数小于或等于位置数字(即N
)时停止。此时,N
就是h指数。换句话说,h指数是指一个研究员有h
篇论文,每篇被引用了至少h
次,而其余的论文引用次数不超过h
次。
例子1: 引用次数为 (1,100,2,3),排序后为 (100,3,2,1):
- 第1篇论文引用次数100,至少有1篇论文引用次数不少于1(满足);
- 第2篇论文引用次数3,至少有2篇论文引用次数不少于2(满足);
- 第3篇论文引用次数2,至少有3篇论文引用次数不少于3(不满足)。
因此,h指数为2。
例子2: 引用次数为 (1,100,3,3),排序后为 (100,3,3,1):
- 第1篇论文引用次数100,至少有1篇论文引用次数不少于1(满足);
- 第2篇论文引用次数3,至少有2篇论文引用次数不少于2(满足);
- 第3篇论文引用次数3,至少有3篇论文引用次数不少于3(满足);
- 第4篇论文引用次数1,至少有4篇论文引用次数不少于4(不满足)。
因此,h指数为3。
h指数试图为研究者的学术影响力和成果的广度及深度提供一个统一的评价标准,它同时考虑了发表论文的数量与论文的学术影响力。
二分查找
// 引入所有标准库的头文件
#include<bits/stdc++.h>
using namespace std;
// 定义一个足够大的常量 z,用于存储论文的引用次数
const int z=1e5+10;
// 定义全局变量,用于存储论文数量和每篇论文的引用次数
int n,k;
int c[z];
// 定义一个辅助函数 check,用于判断在给定的 h 指数 mid 下,Bessie 是否能够达到或超过这个 h 指数
bool check(int mid)
{
int a=0,b=0; // a 用于统计引用次数不小于 mid 的论文数量,b 用于统计引用次数为 mid-1 的论文数量
for(int i=0; i<n; i++) // 遍历所有论文
{
if(c[i]>=mid) a++; // 如果论文的引用次数不小于 mid,则 a 增加
if(c[i]==mid-1) b++; // 如果论文的引用次数恰好为 mid-1,则 b 增加
}
// 如果 a 加上 b 的最小值(考虑 k 的限制)不小于 mid,则返回 true,表示可以达到或超过 h 指数
if((a+min(b,k))>=mid) return true;
// 否则返回 false,表示无法达到或超过 h 指数
return false;
}
// 主函数
int main()
{
// 读取论文数量 n 和 Bessie 可以引用的论文数量 k
cin>>n>>k;
// 读取每篇论文的引用次数
for(int i=0; i<n; i++)
cin>>c[i];
// 初始化二分查找的左右边界
int l=0,r=n;
// 进行二分查找——右查找,查找最大值
while(l<r)
{
// 计算二分查找的中间值 mid
int mid=(l+r+1)>>1;// 注意这里的 mid 计算方式是 (l + r + 1) / 2,防止溢出
// 如果当前的 h 指数 mid 满足条件,则左边界更新为 mid
if(check(mid)) l=mid;
// 否则右边界更新为 mid - 1
else r=mid-1;
}
// 输出最终找到的最大的 h 指数
cout<<r<<endl;
// 程序结束
return 0;
}
这段代码通过二分查找算法来找到 Bessie 在写完综述后可以达到的最大 h 指数。二分查找的时间复杂度为 O(log n),其中 n 是论文的数量。代码首先读取输入的论文数量和可以引用的论文数量,然后读取每篇论文的引用次数。接着,通过二分查找确定最大的 h 值,使得至少有 h 篇论文的引用次数不小于这个 h 值。最后,输出这个 h 值作为结果。
需要注意的是,代码中的 const int z=1e5+10;
定义了一个非常大的数,这是为了确保数组 c
有足够的空间来存储所有可能的引用次数。在实际应用中,这个值可以根据实际情况进行调整。此外,二分查找中的 mid
计算方式 (l + r + 1) / 2
是为了避免在计算过程中发生整数溢出。
贪心+双指针
#include<bits/stdc++.h> // 包含常用库的头文件
using namespace std; // 使用标准命名空间
const int z=1e5+10; // 定义常量z为100010,稍大于输入数据的上限以方便索引
int c[z]; // 定义数组c,用来存储每篇论文的引用次数
int main() // 主函数入口
{
int n,l; // n表示论文的数量,l表示综述中能够引用的最大论文数
cin>>n>>l; // 输入n和l的值
for(int i=1;i<=n;i++) // 循环读入每篇论文的引用次数
cin>>c[i];
sort(c+1,c+n+1,greater<int>()); // 将引用次数以降序排序
int res=0; // 初始化结果res为0,用来记录最大的h指数
for(int i=1,j=n;i<=n;i++) // 循环通过引用次数计算h指数,i从1到n
{
while(j>0&&c[j]<i) j--; // 如果引用次数小于当前的i,j减小,直到c[j]大于等于i或j为0
if(c[i]>=i-1&&i-j<=l) // 检查是否可以将i作为新的h指数
res=i; // 如果当前i满足条件,更新h指数为i
}
cout<<res; // 输出最大的h指数
return 0; // 程序结束
}
代码的核心逻辑是:
- 读取论文数量
n
和综述中可以引用的论文数量l
。 - 读取每篇论文的引用次数并存储在数组
c
中。 - 对数组
c
进行降序排序,以便之后计算 h 指数时能够从引用次数高的开始考虑。 - 初始化
res
为 0,用来记录最终可能的最大 h 指数。 - 开始遍历每个可能的 h 指数
i
,从 1 到n
。- 使用变量
j
从数组末尾开始向前移动,直到找到c[j]
不小于i
。这样就确定了有j
篇论文至少被引用了i
次。 - 检查条件
c[i] >= i - 1
和i - j <= l
是否都满足,如果满足,就将res
更新为i
。c[i] >= i - 1
确保了当前考虑的论文至少有i - 1
次引用。i - j <= l
确保了为了达到 h 指数为i
,至多只需要额外引用l
篇论文。
- 使用变量
- 当遍历完所有可能的 h 指数后,输出最大的 h 指数
res
。
难点解释
首先我们将c 从大到小排序,然后我们从小到大枚举h 值,且当前h 值也代表前h 篇论文引用次数符合要求,因为前h 篇论文的引用次数一定都是最大的,考虑后面的一定不会更优。
那么如何判断前h 篇符合要求呢?首先这些论文中最小的引用次数一定要大于等于h − 1 ,否则就算额外引用一次也达不到要求,这样就没有h 篇论文符合要求了;其次这些论文中引用次数为h − 1 的论文数要小于等于L,这样才能将所有引用次数为h − 1 的论文变为h 次。
由于我们从小到大枚举h 值,也就是从前往后枚举每个论文,因此当前枚举到的c [ i ] 一定是最小值,所以第一个判断就是c [ i ] ≥ i − 1 ;假设j 指向的是从右往左第一个大于等于h 值的论文,即j 右边的论文h 值都小于当前的h ,而我们已经判断了c [ i ] ≥ i − 1 且其左边的数一定大于等于c [ i ] ,那么如果j 在i 的左边,说明其间的所有数均为h − 1 (不包括c [ j ] ),那么其数量就为i − j ,因此第二个判断就是i − j ≤ L。当i 也就是h 值增加的时候,j 一定会往左移动,也就是c [ j ] 得不断变大,因此可以用双指针实现。