Moo University - Financial Aid POJ - 2010 (优先队列 二分)

在这里插入图片描述

题意: 每头牛有价格和价值两个属性, 有F资金, 从C头牛中选出N头(N为奇数), 求C头牛中最大的价值中位数

题解: 对于这种题, 八成猜也可以猜到就是二分/优先队列了, 我们需要考虑的, 就是从哪个角度入手.
对于中位数这种东西, 排个序, 我们完全是可以二分来求的, 再考虑一下check函数, 因为同时引入了价格和价值两个属性, 所以略微有些麻烦啊. 我们二分价值, 再对做一遍 n 的检验, 中位数左右的总价格和数量是否达标, 应该是会分为四种情况, 再依次判断
具体的看代码吧

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const LL maxn = 1e5+10;

struct node{
    int s, a, i;
}cow[maxn], aid[maxn];
bool cmp1(node a, node b){ return a.s < b.s;}
bool cmp2(node a, node b){ return a.a < b.a;}
int N, C, F, ans = -1; //M为中位下标, cntI为中位前后的数量
int main()
{
    scanf("%d%d%d",&N,&C,&F);
    for(int i = 1; i <= C; i++)
        scanf("%d%d",&cow[i].s,&cow[i].a);

    sort(cow+1, cow+1+C, cmp1);
    for(int i = 1; i <= C; i++)
        cow[i].i = i;
    memcpy(aid+1, cow+1, sizeof(node)*C);
    sort(aid+1, aid+1+C, cmp2);

    int l = 1, r = C;
    //二分查找值
    while(l < r){
        int mid = (l+r)/2;
        //计算以cow[mid]为中点时, 左右的总价格, 数量
        int left = 0, right = 0, sum = cow[mid].a, half = N/2;
        for(int i = 1; i <= C; i++){
            if(aid[i].i<mid && left<half && sum+aid[i].a<=F)
                sum+=aid[i].a, ++left;
            else if(aid[i].i>mid && right<half && sum+aid[i].a<=F)
                sum+=aid[i].a, ++right;
        }

        if(left<half && right<half) //情况1: 数量不够, 无法取得中位数
            break;
        else if(left<half && right>=half) //情况2: 偏左
            l = mid+1;
        else if(left>=half && right<half) //情况3: 偏右
            r = mid;
        else if(left>=half && right>=half) //情况4: 可以取得中位数, 试试更大的
            ans = max(cow[mid].s, ans), l = mid+1;
    }
    printf("%d\n",ans);

	return 0;
}

再说一下这道题的优先队列思路
同样先排序, 考虑每个奶牛作为中位数时,比它分数低(前面的)的那群牛的学费总和lower_i,后面的总和upper_i。然后从分数高往分数低扫描,满足aid_i + lower_i + upper_i <= F的第一个解就是最优解。
代码暂且省略啦

通过这道题我们可以发现, 很多时候优先队列/二分这种logn的优化方法是可以互换的

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/87449590