看到牛客的每日一题挺有意思,决定刷一刷看看。
本来想从后往前的,一看发现有点难,还是从头开始吧,应该前面简单点吧。
先做第一期的第一题tokitsukaze and Soldier(3月25日)
题目是这样的,从N个士兵中挑选一些组成一个团,每个士兵有一个战力v[i]和一个要求s[i],这里的s[i]表示第i个士兵要求团里最多只有s[i]个人他才参加。问最高战力是多少?
士兵人数n<=10^5
题目类型:
贪心、堆(优先队列)
思路:
一开始是想着能不能排个序贪心看看,结果发现不行。
让我们分析一下,假设参加的人数为k,那么我们只需要从满足s[i]>=k的人中挑选战力最高的k个人就可以了。
这个性质我们很容易想到用堆解决。
我们可以对k从大到小进行枚举,对于当前枚举的k,我们可以知道s[i]==k的人,由于k是从大到小枚举的,因此s[i]>k的我们已经放到堆里了。那么我们只要把这部分人放到堆中,然后从大到小取出k个求和就可以了。
这里由于堆的性质,我们需要频繁进行pop和push,这样会导致超时。
所以我们稍微改进一下,我们始终维护堆中最大的k个元素,当有新加入的元素时先加入,然后我们从堆中删除最小的元素直到剩下k个元素,过程中我们可以计算和。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int d[100005],cnt;
int n;
ll maxians;
struct node{
int v,s;
}p[100005],q[100005];
bool cmp1(node a,node b){
if(a.s==b.s)return a.v>b.v;
return a.s<b.s;
}
struct cmp2{
bool operator()(node a,node b){
return a.v>b.v;
}
};
priority_queue<node,vector<node>,cmp2>qu;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>p[i].v>>p[i].s;
sort(p+1,p+1+n,cmp1);
// for(int i=1;i<=n;i++)cout<<p[i].v<<' '<<p[i].s<<endl;
for(int i=1;i<=n;i++){
if(p[i].s!=p[i-1].s)d[++cnt]=p[i].s;
}
int la;
ll tot=0;
for(int i=n;i>=1;i--){
if(d[cnt]==p[i].s)qu.push(p[i]),tot+=p[i].v;
else {
la=i;
break;
}
}
for(int k=cnt-1;k>=1;k--){
int people=d[k];
while(people<qu.size()){
node now=qu.top();
tot-=now.v;
qu.pop();
};
for(int i=la;i>=1;i--){
if(p[i].s==people){
node now=qu.top();
if(p[i].v>now.v){
qu.pop();
tot-=now.v;
tot+=p[i].v;
qu.push(p[i]);
}
}
else {
la=i;
break;
}
}
maxians=max(maxians,tot);
}
cout<<maxians<<endl;
//*/
return 0;
}
京公网安备 11010502036488号