一道题目的另解

题目描述

由于对计算机科学的热爱,以及有朝一日成为 「Bessie 博士」的诱惑,奶牛 Bessie 开始攻读计算机科学博士学位。

经过一段时间的学术研究,她已经发表了N篇论文,并且她的第i篇论文得到了来自其他研究文献的C{i}次引用。

Bessie 听说学术成就可以用H指数来衡量。

H指数等于使得研究员有至少H篇引用次数不少于H的论文的最大整数H

为了提升她的H指数,Bessie 计划写一篇综述,并引用一些她曾经写过的论文。

由于页数限制,她至多可以在这篇综述中引用L篇论文,并且她只能引用每篇她的论文至多一次

请帮助 Bessie 求出在写完这篇综述后她可以达到的最大H指数。

注意 Bessie 的导师可能会告知她纯粹为了提升H指数而写综述存在违反学术道德的嫌疑;我们不建议其他学者模仿 Bessie 的行为。

输入格式 

输入第一行包含N,L

第二行包含N个空格隔开的数C_{1},C_{2}, ... ,C_{N}

输出格式

共一个数,表示写完综述后Bessie可以达到的最大H指数。

题目链接:

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

解题思路:

  1. 先从大到小排序一次, 若ci比当前的位置i(1 <= i <= n)大,则符合h值条件
  2. 当ci < i,我们就找到了提高h值的边界位置,从该位置往前尽可能增加引用值,即增加引用值比它大或相等的论文的引用量。比如下面的例子:
  3. 重新排序(因为_增加部分论文引用量后,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;
}

解法二:差分

这种方法和我的上一篇文章有着相似之处:

一道经典题目的新方法-CSDN博客

思路就是:用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],故:

\bigtriangleup S_{i}=max(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;
}

  • 39
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值