堆——奶牛大学nkoj2294

分析过程:
     要求选出的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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值