POJ 2010 Moo University - Financial Aid(贪心+优先队列)

题目大意:

C个奶牛,两个属性:分数和金额

在金额总和不超过F的情况下,选出N个奶牛,使得N个奶牛的分数的中位数尽可能大

玄学ac……数据可能比较弱吧,这个思路有点问题T_T

思路:

因为要取得最大的中位数,所以有N/2个分数对结果没影响,这部分尽可能取钱最少的,先按金额排序,贪心的取最小的N/2个。然后剩下的按分数从大到小排序,维护一个N/2+1的区间,若金额不够就取出金额最大的,放入下一个,直到金额够取,最后全部排序输出中位数。

代码:

​

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100000;
const int maxc=20000;
int n,f,c,a[maxn],b[maxn],r[maxn];		//a:分数,b:钱
bool vis[maxn];							//标记第一次取过的奶牛 
vector<int> ans;						//存最后取的所有奶牛 
struct node								//优先队列节点 
{
	int pos,s,m;
};
struct cmp					//优先队列排序,优先出钱最多的 
{
	bool operator()(node a,node b)
	{
		return a.m<b.m;
	}
};
priority_queue<node,vector<node>,cmp> q;
bool cmp1(int x,int y)		//按钱数间接排序 
{
	return b[x]<b[y];
}
bool cmp2(int x,int y)		//按分数间接排序 
{
	return a[x]>a[y];
}
int main()
{
	while(~scanf("%d%d%d",&n,&c,&f))
	{
		memset(vis,0,sizeof(vis));
		for(int i=0;i<c;i++)
		{
			scanf("%d%d",&a[i],&b[i]);
		}
		for(int i=0;i<=c;i++) r[i]=i;
		sort(r,r+c,cmp1);
		int res=n>>1;
		for(int i=0;i<res;i++)		//取N/2个 
		{
			int e=r[i];
			f-=b[e];
			vis[e]=true;
			ans.push_back(a[e]);
		}
		res=n-res;
		for(int i=0;i<=c;i++) r[i]=i;
		sort(r,r+c,cmp2);
		int p,cnt=0,sum=0;
		for(p=0;p<c;p++)			//取N/2+1个 
		{
			int e=r[p];
			if(!vis[e])
			{
				cnt++; vis[e]=true;
				node temp;
				temp.m=b[e]; temp.pos=e; temp.s=a[e];
				sum+=b[e];
				q.push(temp);
			}
			if(cnt==res) break;
		}
		while(sum>f)				//维护到钱够取的情况 
		{
			p++;
			if(p==c) break;
			int e=r[p];
			node temp1=q.top();
			if(temp1.m>b[e]&&!vis[e])
			{
				q.pop();
				node temp2;
				temp2.m=b[e]; temp2.pos=e; temp2.s=a[e];
				q.push(temp2);
				sum-=temp1.m;
				sum+=temp2.m;
			}
		}
		if(sum<=f)
		{
			while(!q.empty())
			{
				node temp;
				temp=q.top(); q.pop();
				ans.push_back(temp.s);
			}
			sort(ans.begin(),ans.end());
			printf("%d\n",ans[n/2]);
		}
		else printf("-1\n");
	}
}

后来想了想每次弹出钱数最高的可能不太合理,可能弹出一个钱数不是最大的再取一个也能使钱数满足条件且钱数最大那个恰好是中位数最大那个。

想了组数据

5  6  21

9  1

8  1

7  9

6  7

5  4

4  6

果然答案不合适:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值