POJ-2010:Moo University - Financial Aid(优先队列,中位数最大化)

题目

一共 C C C个学生,学校要录 N N N(奇数)个,每个学生的分数和需要的 a i d aid aid给出,学校最多能提供 F F F资金,求录取的学生中分数中位数最大。

主要思路

难点在于此题求的是最大的中位数。既然是求中位数,那先来一个sort好了。sort完陷入沉思------小弱鸡从每求过中位数怎么办,脑海中浮现出爆搜,回头一看数据范围我怕了,还是去看题解把。

看完大佬的思路感觉好像不复杂,让排序后的数组中每个元素都体验一次中位数的感觉,也就是说在他左侧选 m i d mid mid个元素 m i d = N / 2 − 1 mid=N/2-1 mid=N/21,在右侧也选 m i d mid mid个元素,如果这时候左右两侧所有牛需要的 a i d aid aid之和加上当前牛需要的 a i d aid aid不超界,就是一个正确答案,在所有正确答案中选最大的那个(由于是排过序的,从右往左第一个合格数据就是答案)

确定中位数,左右两侧的小伙伴怎么选呢

当然是选 a i d aid aid最小的 m i d mid mid个元素,最大化成功率,最小 k k k元素,是时候放一只大顶堆了。第一次写了如下代码,满心欢喜的等着AC

#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cmath>
#include<string.h>
#include<iomanip>
#include<algorithm>
using namespace std;

#define ll long long
#define Max 100005
#define inf 2000000000//100个cow的智慧都是1000,最大情况100*1000
#define p pair<ll,ll>

struct cow {
	ll score, aid;
	bool operator<(cow c) {
		return score < c.score;
	}
}cows[Max];

ll n, c, f;//要选的,总共的,救济金总额
int main() {
	ll left[Max], right[Max];//以i做中位数,他的左侧aid最小值之和,右侧aid最小值之和
	cin >> n >> c >> f;
	for (ll i = 0; i < Max; i++) { left[i] = inf; right[i] = inf; }
	for (ll i = 0; i < c; i++) cin >> cows[i].score >> cows[i].aid;
	sort(cows, cows + c);
	ll mid = n / 2, sum = 0;
	for (ll i = 0; i < c; i++)//遍历所有数组元素
	{
		priority_queue<ll> pris;
		//update array left
		for (ll j = 0; j < i; j++) {
			if (pris.size() < mid) pris.push(cows[j].aid);
			else if (cows[j].aid < pris.top())//比最大的aid小{
			{
				pris.pop(); pris.push(cows[j].aid);
			}
		}
		if (pris.size() == mid) {//既然是中位数,那么左边的数必须够这个数目
			while (!pris.empty()) { sum += pris.top(), pris.pop(); }
			left[i] = sum; sum = 0;
		}
		//update array right
		for (ll j = i + 1; j < c; j++) {
			if (pris.size() < mid) pris.push(cows[j].aid);
			else if (cows[j].aid < pris.top())//比最大的aid小{
			{
				pris.pop(); pris.push(cows[j].aid);
			}
		}
		if (pris.size() == mid) {
			while (!pris.empty()) { sum += pris.top(), pris.pop(); }
			right[i] = sum; sum = 0;
		}
	}
	for (ll i = c - 1; i >= 0; i--) {
		if (cows[i].aid + left[i] + right[i] <= f)
		{
			cout << cows[i].score << endl;
			return 0;
		}
	}
}

直到看见了TLE,我?????仔细一想对每个元素都单独判断,复杂度 O ( n 2 ) O(n^2) O(n2),ORZ,怎么优化呢。其实一次遍历就好了

#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cmath>
#include<string.h>
#include<iomanip>
#include<algorithm>
using namespace std;

#define ll long long
#define Max 100005
#define inf 2000000000//100个cow的智慧都是1000,最大情况100*1000
#define p pair<ll,ll>

struct cow {
	ll score, aid;
	bool operator<(cow c) {
		return score < c.score;
	}
}cows[Max];

ll left1[Max], right1[Max];//以i做中位数,他的左侧aid最小值之和,右侧aid最小值之和
ll n, c, f;//要选的,总共的,救济金总额
int main() {
	cin >> n >> c >> f;
	for (ll i = 0; i < c; i++) cin >> cows[i].score >> cows[i].aid;
	sort(cows, cows + c);
	ll mid = n / 2, suml = 0, sumr = 0;
	priority_queue<ll> pl, pr;
	for (ll i = 0; i < c; i++)//遍历所有数组元素
	{
		//左右夹击
		left1[i] = (pl.size() == mid) ? suml : inf;
		right1[c - i - 1] = (pr.size() == mid) ? sumr : inf;
		pl.push(cows[i].aid); pr.push(cows[c - i - 1].aid);
		suml += cows[i].aid; sumr += cows[c - i - 1].aid;
		if (pl.size() > mid) {
			suml -= pl.top(); pl.pop();
		}
		if (pr.size() > mid) {
			sumr -= pr.top(); pr.pop();
		}
	}
	for (ll i = c - 1; i >= 0; i--) {
		if (cows[i].aid + left1[i] + right1[i] <= f)
		{
			cout << cows[i].score << endl;
			return 0;
		}
	}
	cout << -1 << endl; return 0;
}

l e f t left left数组从左往右维护到第 i i i个元素位置的最小 m i d mid mid个元素, r i g h t right right恰好相反,维护从 n − 1 n-1 n1开始,到 n − i − 1 n-i-1 ni1位置的最小 m i d mid mid个元素,一次更新完两个数组,最后特判-1,成功AC,收工睡觉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值