poj 2010--Moo University - Financial Aid

9 篇文章 0 订阅
2 篇文章 0 订阅

给出c组数,要在其中选n个(n为奇数),使其分数的中位数最大,同时这n个数的费用之和不能超过f,如果不能满足就输出-1

http://poj.org/problem?id=2010

这道题想了好久。。WA了好几遍,还TLE了一次>_<

其实就是大根堆,可以用stl中的优先队列

先按分数排序,

然后用一个数组dp[i][j], dp[i][0]表示i之前费用最小的n/2个数之和,dp[i][1]表示i之后费用最小的n/2个数之和

将n/2个数保存在大根堆里

先从前往后扫一遍,求出dp[i][0],更新的时候和堆的根比较(也就是优先队列的队首)。如果小于根,就将根删除,将更小的元素加进去

再从后向前扫一遍,同理求出dp[i][1]。

(我一直纠结与如何同时求出dp[i][0]和dp[i][1]。。真是。。。(@﹏@)~ )

最后再从后向前扫一遍,找到满足条件的最大值即可~

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll n, c, f;
ll dp[100000 + 10][2];
struct node{
    ll sco, w;
}cow[100000 + 10];
bool cmp(node a, node b){
    return a.sco < b.sco;
}
int main(){
    scanf("%I64d%I64d%I64d", &n, &c, &f);
    for(int i=0; i<c; i++){
        scanf("%I64d%I64d", &cow[i].sco, &cow[i].w);
        dp[i][0] = dp[i][1] = 0;
    }
    priority_queue<ll> Q1, Q2;
    ll ans = -1, tmp = n / 2;
    sort(cow, cow + c, cmp);
    for(int i=0; i<n/2; i++){
        dp[tmp][0] += cow[i].w;
        Q1.push(cow[i].w);
    }
    for(int i=tmp+1; i<c; i++){
        ll now = Q1.top();
        dp[i][0] = dp[i - 1][0];
        if(cow[i - 1].w < now){
            Q1.pop();
            Q1.push(cow[i -1].w);
            dp[i][0] += cow[i - 1].w - now;
        }
    }
    tmp = c - 1 - n / 2;
    for(int i=c-1; i>tmp; i--){
        dp[tmp][1] += cow[i].w;
        Q2.push(cow[i].w);
    }
    for(int i=tmp-1; i>=0; i--){
        ll now = Q2.top();
        dp[i][1] = dp[i + 1][1];
        if(cow[i + 1].w < now){
            Q2.pop();
            Q2.push(cow[i + 1].w);
            dp[i][1] += cow[i + 1].w - now;
        }
    }
    for(int i=tmp; i>=n/2; i--){
        if(dp[i][0] + dp[i][1] + cow[i].w <= f){
            ans = cow[i].sco;
            break;
        }
    }
    printf("%I64d\n", ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值