牛客网【每日一题】3月25

题号:NC50439
名称: tokitsukaze and Soldier
来源:练习赛50-C
链接标题 链接:https://ac.nowcoder.com/acm/problem/50439

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述

在一个游戏中,tokitsukaze需要在n个士兵中选出一些士兵组成一个团去打副本。
第i个士兵的战力为v[i],团的战力是团内所有士兵的战力之和。
但是这些士兵有特殊的要求:如果选了第i个士兵,这个士兵希望团的人数不超过s[i]。(如果不选第i个士兵,就没有这个限制。)
tokitsukaze想知道,团的战力最大为多少。
输入描述:

第一行包含一个正整数n(1≤n≤10^5)。
接下来n行,每行包括2个正整数v,s(1≤v≤10^9,1≤s≤n)。

输出描述:

输出一个正整数,表示团的最大战力。

示例1
输入

2
1 2
2 2

输出

3

示例2
输入

3
1 3
2 3
100 1

输出

100

思路
所求的最大战斗力由v和s这两个因素限制。
结构体数组a存放v和s,然后对其排序,先按照s从大到小,如果s相同再排v,也是从大到小(先要保证足够的人数,后面好进行取舍)。
定义一个从小到大的优先队列q,ans相互跟随q,(q每次添加一个战力值,同时用ans加上战力值;ans删去,q也弹出),战力值的先后加入由s的排序决定,当q的元素数量大于当前s的值时(s是由大到小的排序的),就将多出的部分pop出(q是从小到大排序,所以弹出的总是其中最小值),再用ans减去,这样ans的值即为在人数限定在s内的最佳情况(因为弹出的是最小值,相对于前s个数的和,所以之后也不用再考虑被弹出的数),记录ans的最大值
我刚开始提交是70多分,想了一阵子才发现是数组开小了(笑哭)

#include<map>
#include<queue>
#include<bits/stdc++.h>
#define maxn 100004
using namespace std;
struct node{
    long long int v,s;    
};
node a[maxn];
bool cmp(node a,node b)
{
    if(a.s!=b.s)return a.s>b.s;
    else return a.v>a.v;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].v>>a[i].s;
    }
    sort (a+1,a+1+n,cmp);
//    int tot=0;
    long long int ans=0;
    int minn=maxn;
    long long int maxx=0;
    priority_queue<int,vector<int>,greater<int> >q;
    for(int i=1;i<=n;i++)
    {
        q.push(a[i].v);
        ans+=a[i].v;
        while(q.size()>a[i].s)
        {

            ans-=q.top();
//            printf("q.size=%d,a[i].s=%d,ans=%d\n",q.size(),a[i].s,ans);
            q.pop();
//            tot--;
        }
        if(ans>maxx)maxx=ans;
    }

    cout<<maxx;
    return 0;
}