分析过程:
要求选出的N头牛的成绩的中位数尽可能大,我们可以考虑依次讨论每头奶牛的成绩是否适合作为中位数。
一.先按高考分数Score由小到大排序:
显然,排序后,能够作为答案的成绩一定在[n/2+1...C-n/2]这个下标范围之间。
二.若k位于这个范围[n/2+1...C-n/2],那么Score[k]是否是一个合理的中位数呢?
既然k是选出的中位数的下标,也就意味着:
在[1...k-1]间定要选出n/2头牛,我们希望选总学费尽量少n/2头奶牛,设该学费总额为Left[k]
在[k+1...C]间定也要选出n/2头牛,我们也希望选总学费尽量少n/2头奶牛,设该学费总额为Right[k]
若Left[k]+Right[k]+Money[k]<=F,则Score[k]为一个可行的答案。
最终找出满足Left[k]+Right[k]+Money[k]<=F的最大的一个k,它对应的Score[k]即为答案。
三.怎样快速求出[n/2+1...C-n/2]区间中每一个数对应的Left[ ]和Right[ ]值呢?
1.建立一个大根堆,把最左边的n/2头牛的学费Money[ ]存到堆中,并记录下堆中奶牛的学费总和Sum。
2.依次讨论n/2+1到C-n/2这段区间的奶牛:
<1>若当前讨论的第k头牛,则Left[k]=Sum。
<2>若Money[k]小于堆顶元素,则用Money[k]替换堆顶元素,调整堆,并且更新它们的总和Sum
<3>继续讨论下一头奶牛(第k+1头)
3.求Right[ ]值与求Left[ ]同理
四.时间复杂度
排序O(ClogC)+讨论 O(ClogC)
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{
int score;
int money;
int left;//在这只奶牛左边找出n/2只奶牛的所需资金最小值
int right;
}cow[100005];
bool compare(node a,node b)//按照每只奶牛的SAT分数进行排序
{
return a.score<b.score;
}
priority_queue<int>q;
int n,c,f,sum=0;
int main(){
int i,j,k,temp,ans=0;
cin>>n>>c>>f;
for(i=1;i<=c;i++)
{
scanf("%d%d",&cow[i].score,&cow[i].money);
}
sort(cow+1,cow+1+c,compare);
for(i=1;i<=n/2;i++)//先把前n/2只奶牛进堆
{
q.push(cow[i].money);
sum=sum+cow[i].money;
}
for(k=n/2+1;k<=c-n/2;k++)//要使第k只牛能够成为中位数,他的左边至少要有n/2只牛,右边同理
{
cow[k].left=sum;
temp=q.top();
if(cow[k].money<q.top())//如果cow[k].money<堆顶元素更新堆顶元素及sum
{
q.pop();
q.push(cow[k].money);
sum=sum-temp+cow[k].money;
}
}
while(q.size())q.pop();sum=0;
for(i=c;i>=c-n/2+1;i--)
{
q.push(cow[i].money);
sum=sum+cow[i].money;
}
for(k=c-n/2;k>=n/2+1;k--)
{
cow[k].right =sum;
temp=q.top();
if(cow[k].money<q.top())
{
q.pop();
q.push(cow[k].money);
sum=sum-temp+cow[k].money;
}
}
for(k=n/2+1;k<=c-n/2;k++)
{
if(cow[k].left+cow[k].right+cow[k].money<=f)ans=cow[k].score;
}
if(ans==0)cout<<"-1";
else cout<<ans;
}