POJ 2010 二分找中位数

从总共C个备选项中挑选出N个

每个备选项有两个属性  A, B

要使选出的N个的A的中位数尽量大, 并且所有选出的B的和 小于等于 F



这道题一开始我是在二分专题看到的, 然而并想不出如何二分

用一种我现在也不知道是什么样的方法A了, 不过这个不太重要,

主要是二分的做法 


二分的话有两个难点, 无法解决

①显然只要能各选出N/2个大于等于X,    N/2个小于等于X的数

那么X就是可行的 lft = mid

然而 如果两个都找不全, 我们是应该把X调大 还是调小呢?

②和X一样大的数,显然是两边都可以放的, 那么到底放哪里

用三个优先队列可能可以处理,但实在是太麻烦了



当时我A了找到一个 二分的题解,大致看了一下,好像会了

然而在优先队列看到这道题,发现自己原来并不懂怎么做


http://www.cnblogs.com/Thispoet/archive/2011/11/28/2266853.html

这是题解的链接, 做法是先按分数排序,给每个备选项从小到大编号,这样就从求分数的中位数 转化为了求编号的中位数了

二分判断可行性{

先按照费用全都排序一下

然后只要能各选出 N/2个 就是可行的

//如果不可行的话 有不同的返回值

}

有一点正确性需要证明

有没有可能 A==X 的某个数 必然属于 小于等于 或者 大于等于的那组呢

假设一个情景 

两组各找到了B最小的N/2个数 但 其总和超过了F  

然而假如把其中一组(排序后应该是小于等于这一组的)中有一些A==X且没用到的备选项(abcd)放到另外一组其费用就会降低,就是可行的

显然我们保持小于等于的那一组不变 把中位数定为 a      check出来结果显然就是可行的


整个程序处理的第一步应该是 找出B最小的N个数 如果其总和已经超过F了,则必然不存在解 反之必然存在解

有趣的事情:

二分过程中至少有一组是能找满的




//这道题欠一份题解
//思路和代码都有抄别人的嫌疑…… 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 4;
struct Cow{
	int score, fee, id;
}c[2][maxn];
bool cmp1(const Cow &A, const Cow &B){
	if (A.score == B.score) return A.fee < B.fee;
	return A.score < B.score;
}
bool cmp2(const Cow &A, const Cow &B){
	return A.fee < B.fee;
}
int k, n, aid;
int check(int index){
	int res = aid - c[0][index].fee;
	int lft = 0, rght = 0;
	for (int i = 1; i <= n; ++i){
		if (c[1][i].id == index) continue;
		if (res < c[1][i].fee)  break;
		if (c[1][i].id < index && lft < k){
			res -= c[1][i].fee;
			++lft;
		}
		else if (c[1][i].id > index && rght < k){
			res -= c[1][i].fee;
			++rght;
		}
	} 
	if (lft < k) return 1;//调大了可能行
	if (rght < k) return 2;//调小了可能行
	return 3; 
}
int main(){
	ios::sync_with_stdio(false);
	int i, j, kase;
	while(cin >> k >> n >> aid){
		k /= 2;
		for (i = 1; i <= n; ++i) cin >> c[0][i].score >> c[0][i].fee;
		sort(c[0]+1, c[0]+n+1, cmp1);
		for (i = 1; i <= n; ++i) c[0][i].id = i;
		memcpy(c[1], c[0], sizeof c[0]);
		sort(c[1]+1, c[1]+1+n, cmp2);
		long long sum = 0;
		for (i = 1; i <= (k*2) + 1; ++i) sum += c[1][i].fee;
		if (sum > 1LL * aid){
			cout << -1 << endl;
			continue;
		}
		int lft = 1, rght = n;
		while(lft < rght){
			int mid = (lft + rght) / 2 + 1;
			int signal = check(mid);
			if (signal == 3) lft = mid;
			else if (signal == 1) lft = mid + 1;
			else if (signal == 2) rght = mid - 1;
		}
//		if (check(lft) != 3) cout << -1 << endl;
		cout << c[0][lft].score << endl;
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值