题目描述
由于对计算机科学的热爱,以及有朝一日成为 「Bessie 博士」的诱惑,奶牛 Bessie 开始攻读计算机科学博士学位。
经过一段时间的学术研究,她已经发表了篇论文,并且她的第
篇论文得到了来自其他研究文献的
次引用。
Bessie 听说学术成就可以用指数来衡量。
指数等于使得研究员有至少
篇引用次数不少于
的论文的最大整数
。
为了提升她的指数,Bessie 计划写一篇综述,并引用一些她曾经写过的论文。
由于页数限制,她至多可以在这篇综述中引用篇论文,并且她只能引用每篇她的论文至多一次。
请帮助 Bessie 求出在写完这篇综述后她可以达到的最大指数。
注意 Bessie 的导师可能会告知她纯粹为了提升指数而写综述存在违反学术道德的嫌疑;我们不建议其他学者模仿 Bessie 的行为。
输入格式
输入第一行包含
第二行包含个空格隔开的数
输出格式
共一个数,表示写完综述后Bessie可以达到的最大指数。
题目链接:
https://www.acwing.com/problem/content/3748/
解法一:
1 2 3 4 5 —可能的h值
5 4 2 1 1 —ci值从大到小排序
很容易发现, 因为ci = 2 < 3, 故引用该篇论文后可使h值可提升到3
但因为一篇论文只能引用一次,最多只能提升1h值
能提升1h值 当且仅当 边界位置的论文的引用量及其前面引用量比它高的l-1篇论文的引用值+1后,符合h值的条件 ————至少 h 篇引用次数不少于 h 的论文的最大整数 h
解题思路:
- 先从大到小排序一次, 若ci比当前的位置i(1 <= i <= n)大,则符合h值条件
- 当ci < i,我们就找到了提高h值的边界位置,从该位置往前尽可能增加引用值,即增加引用值比它大或相等的论文的引用量。比如下面的例子:
- 重新排序(因为_增加部分论文引用量后,ci的大小顺序可能变化_),得到h值并输出结果
例子:
l = 2
1 2 3 4 5 ----- 可能的h值
4 3 3 3 1 ----- ci值从大到小排序
4 3 4 4 1 ----- 尽可能增加论文引用量后
4 4 4 3 1 ----- 重新排序,发现h值未增加
l = 3
1 2 3 4 5 ----- 可能的h值
4 3 3 3 1 ----- ci值从大到小排序
4 4 4 4 1 ----- 尽可能增加论文引用量后
4 4 4 4 1 ----- 重新排序,发现h值加1
来自题解:
https://www.acwing.com/solution/content/101195/
代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n, l, i, j;
vector<int>c;
bool cmp(int&a, int&b) //从大到小排序
{
return a > b;
}
int main()
{
cin>>n>>l;
c.resize(n+1); //下标从1开始, 0号不用
for(int i = 1; i < n+1; i++)
cin>>c[i];
sort(c.begin()+1, c.end(), cmp); //从大到小排序
for(i = 1; i < n + 1; i++) //尽可能引用那些阻碍 h 指数提示的论文
{
if(c[i] < i) //找到第一篇不符和的
{
for(j = i; j >= 1 && l > 0; j--, l--) //尽可能往前引用(使h值大的更大,最好都大于当前的i,那就成功提升了)
c[j]++;
break;
}
}
sort(c.begin()+1, c.end(), cmp); //因为引用数的变化,需要重新排序
for(i = 1; i < n+1; i++)
if(c[i] < i) //找到第一篇不符和的,直接输出结果返回
{
cout<<i-1;
return 0;
}
if(i == n + 1) //全满足,那么h值为n
cout<<n<<endl;
return 0;
}
解法二:差分
这种方法和我的上一篇文章有着相似之处:
思路就是:用counting数组表示:引用数为i的文章个数是counting[i]个。
再定义一个数组S[i]表示,引用数大于等于i的文章个数是S[i]个。
通过求counting差分数组得到S[i]。
对于任意一个i而言,多写一篇综述最多让H指数+1。因此,我们让i从1开始遍历,当S[i]>=i时我们就i++,当不满足时,我们看看S[i]经过加入综述这一变化能不能大于等于i。
这个变化量很好求。要让S[i]最大,就要把综述里所有引用都用于counting[i-1]那些文章上。(只有引用数为i-1的文章再加一个1引用数才能变成i,S[i]才有可能变大)。但最终的变化量不能超过L,也不能超过counting[i-1],故:
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int counting[N];
int L;
int S[N];
bool check(int k)
{
if(S[k]>=k) return true;
return false;
}
bool check_add(int k)
{
if(S[k]+min(L,counting[k-1])>=k) return true;
return false;
}
int main(){
int n;
scanf("%d%d", &n,&L);
for (int i = 0; i < n; i ++ ){
int t;
scanf("%d", &t);
counting[t]++;
}
S[0]=n;
for(int i=1;i<N;i++) S[i]=S[i-1]-counting[i-1];
int ans=0;
for (int i = 1; i <= N; i ++ ){
if(check(i)) continue;
else if(check_add(i)){
ans=i;
break;
}else{
ans=i-1;
break;
}
}
printf("%d",ans);
return 0;
}