POJ 2010 Moo University - Financial Aid

大学牛奖学金分配算法

寒假作业的某道题目:
题意:
有很多头牛,要上大学。一开始输入 N,C,F,表示需要总共有C头牛,要录取N头牛,总资助资金为F。
然后输入每头牛的分数和所需资金
然后要录取5头牛,使得总资金不大于F,并且N头牛的分数的中位数最大。
思路:
首先给牛排序,按照分数从小到大排,然后用优先队列(资金大的优先)。因为要求的是中位数,并且N是奇数,那么也就是这头牛的前面将有 N/2头牛,后面也有N/2头牛,那么先从前往后,前N/2头牛的资金加起来,用一个数组去存第i头牛为中间牛时前面的最小资金,所以前N/2头可以直接加起来给第 (N/2 + 1)头牛,然后接下来的牛的资金就和优先队列里的第一个元素(资金最大的)比较,如果资金比最大的那个少,那就把它替换掉,再把资金赋值给下一头牛的资金和。同理也要从后往前进行一次,这样最后只要把牛的前(N/2)资金和 和 后(N/2)资金和相加再加自身资金看是否小于等于F,并且最后在查找的时候按照分数从高到低查找,找到符合的第一个值就可以把它输出了。
代码如下:
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0xFFFFFFF
struct cow{
	int g,v;
	bool operator < (const cow &a) const { //定义堆的优先级 为大根堆
		return v<a.v;
	}
}node[100010];
int n,c,f;
bool cmp(const cow& a,const cow& b){
	return a.g < b.g;		// 让牛们按分数由低到高排 
}
priority_queue<cow>q1,q2;
int minn1[100010],minn2[100010];
int main(){
	int sum1,sum2,ans,half;
	while(scanf("%d %d %d",&n,&c,&f) != EOF){
		for(int i = 1;i <= c;i++)
			scanf("%d %d",&node[i].g,&node[i].v);
		sort(node + 1,node + 1 + c,cmp);
		half = n / 2;		//表示前面和后面各需牛数 
		ans = -1;			// 无结果输出-1 
		sum1 = sum2 = 0;	// 分别用来记录前 (N/2)头牛的最小资金和后(N/2)的 
		memset(minn1,0,sizeof(minn1));
		memset(minn2,0,sizeof(minn2));
		for(int i = 1;i <= c;i++){
			if(i <= half){		// 前 N/2 头可直接相加赋值 
				q1.push(node[i]);
				sum1 += node[i].v;
				continue;
			}
			minn1[i] = sum1;	//把第i头牛前 的资金和赋值 
			if(node[i].v >= q1.top().v)
				continue;		//若是比最大的资金要大则没必要刷新最小资金和 
			sum1 -= q1.top().v;
			q1.pop();			// 肥牛出队 
			sum1 += node[i].v;
			q1.push(node[i]);	// 新牛入队 
		}
		for(int i = c;i >= 1;i--){   //与上同理 
			if(i > c - half){
				q2.push(node[i]);
				sum2 += node[i].v;
				continue;
			}
			minn2[i] = sum2;
			if(node[i].v >= q2.top().v)
				continue;
			sum2 -= q2.top().v;
			q2.pop();			
			sum2 += node[i].v;
			q2.push(node[i]);
		}
		
		for(int i = c - half;i >= half + 1;i--){	// 从后往前,分数从高到低 
			if(minn1[i] + minn2[i] + node[i].v <= f){
				ans = node[i].g;
				break;
			}		
		}
		printf("%d\n",ans);
	}
	return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值