从总共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;
}