优先队列 - Moo University - Financial Aid - POJ - 2010

优先队列 - Moo University - Financial Aid - POJ - 2010

题意:

首 行 输 入 三 个 整 数 n , m , F 首行输入三个整数n,m,F n,m,F

接 着 输 入 m 行 正 整 数 数 对 ( a i , b i ) 。 接着输入m行正整数数对(a_i,b_i)。 m(ai,bi)

从 m 个 数 对 中 挑 选 n 个 , ( a i 1 , b i 1 ) , ( a i 2 , b i 2 ) , . . . , ( a i n , b i n ) , 需 满 足 ∑ k = 1 n b i k ≤ F 。 从m个数对中挑选n个,(a_{i_1},b_{i_1}),(a_{i_2},b_{i_2}),...,(a_{i_n},b_{i_n}),需满足\sum_{k=1}^{n}b_{i_k}≤F。 mn,(ai1,bi1),(ai2,bi2),...,(ain,bin)k=1nbikF

在 满 足 以 上 约 束 条 件 的 前 提 下 , 使 得 数 列 a i 1 , a i 2 , . . . , a i n 的 中 位 数 最 大 。 输 出 最 大 的 中 位 数 。 在满足以上约束条件的前提下,使得数列a_{i_1},a_{i_2},...,a_{i_n}的中位数最大。输出最大的中位数。 使ai1,ai2,...,ain

保 证 n 是 奇 数 。 保证n是奇数。 n

若 无 法 满 足 前 提 条 件 , 输 出 − 1 。 若无法满足前提条件,输出-1。 1

Sample Input:

3 5 70
30 25
50 21
20 20
5 18
35 30

Sample Output:

35

数据范围:

1 ≤ n ≤ 19 , 999 , n ≤ m ≤ 100 , 000 , 0 ≤ F ≤ 2 , 000 , 000 , 000 T i m e   l i m i t : 1000 m s , M e m o r y   l i m i t : 30000 k B 1 ≤ n ≤ 19,999,n ≤m ≤ 100,000,0 ≤ F ≤ 2,000,000,000\\Time \ limit:1000 ms,Memory\ limit:30000 kB 1n19,999nm100,0000F2,000,000,000Time limit:1000msMemory limit:30000kB

分析:

由 于 n 是 奇 数 , 中 位 数 两 侧 的 数 的 个 数 相 同 , 都 是 ⌊ n 2 ⌋ 个 。 由于n是奇数,中位数两侧的数的个数相同,都是\lfloor\frac{n}{2}\rfloor个。 n2n

因 此 , 我 们 可 以 将 数 对 ( a i , b i ) 以 a i 为 第 一 关 键 字 排 序 , 然 后 从 大 到 小 开 始 枚 举 中 位 数 , 判 断 是 否 可 行 即 可 。 因此,我们可以将数对(a_i,b_i)以a_i为第一关键字排序,然后从大到小开始枚举中位数,判断是否可行即可。 (ai,bi)ai

枚 举 中 位 数 后 a i 后 , 相 当 于 a i 两 侧 分 别 挑 选 ⌊ n 2 ⌋ 个 数 对 。 枚举中位数后a_i后,相当于a_i两侧分别挑选\lfloor\frac{n}{2}\rfloor个数对。 aiai2n

记 a i 左 侧 挑 选 的 ⌊ n 2 ⌋ 个 数 对 为 ( a l 1 , b l 1 ) , ( a l 2 , b l 2 ) , . . . , ( a l n 2 , b l n 2 ) , 记 S l = ∑ k = 1 ⌊ n 2 ⌋ b l k 记a_i左侧挑选的\lfloor\frac{n}{2}\rfloor个数对为(a_{l_1},b_{l_1}),(a_{l_2},b_{l_2}),...,(a_{l_\frac{n}{2}},b_{l_\frac{n}{2}}),记S_l=\sum_{k=1}^{\lfloor\frac{n}{2}\rfloor}b_{l_k} ai2n(al1,bl1),(al2,bl2),...,(al2n,bl2n)Sl=k=12nblk

右 侧 挑 选 的 ⌊ n 2 ⌋ 个 数 对 为 ( a r 1 , b r 1 ) , ( a r 2 , b r 2 ) , . . . , ( a r n 2 , b r n 2 ) , 记 S r = ∑ k = 1 ⌊ n 2 ⌋ b r k 右侧挑选的\lfloor\frac{n}{2}\rfloor个数对为(a_{r_1},b_{r_1}),(a_{r_2},b_{r_2}),...,(a_{r_\frac{n}{2}},b_{r_\frac{n}{2}}),记S_r=\sum_{k=1}^{\lfloor\frac{n}{2}\rfloor}b_{r_k} 2n(ar1,br1),(ar2,br2),...,(ar2n,br2n)Sr=k=12nbrk

要 使 得 总 和 不 超 过 F , 至 少 要 满 足 a i 左 右 两 侧 的 S l m i n + S r m i n + b i ≤ F 。 要使得总和不超过F,至少要满足a_i左右两侧的S_{l_{min}}+S_{r_{min}}+b_i≤F。 使FaiSlmin+Srmin+biF

故 我 们 预 处 理 数 组 l [ i ] 来 存 储 在 [ 1 , i − 1 ] 挑 选 ⌊ n 2 ⌋ 个 数 对 的 S l m i n , 故我们预处理数组l[i]来存储在[1,i-1]挑选\lfloor\frac{n}{2}\rfloor个数对的S_{l_{min}}, l[i][1,i1]2nSlmin

用 r [ i ] 来 存 储 在 [ i + 1 , m ] 挑 选 ⌊ n 2 ⌋ 个 数 对 的 S r m i n 。 用r[i]来存储在[i+1,m]挑选\lfloor\frac{n}{2}\rfloor个数对的S_{r_{min}}。 r[i][i+1,m]2nSrmin

问 题 转 化 为 求 某 段 区 间 内 前 ⌊ n 2 ⌋ 小 的 数 的 和 。 问题转化为求某段区间内前\lfloor\frac{n}{2}\rfloor小的数的和。 2n

这 我 们 可 以 通 过 大 根 堆 来 维 护 。 这我们可以通过大根堆来维护。

最 后 仅 需 判 断 l [ i ] + r [ i ] + b [ i ] ≤ F 是 否 成 立 即 可 。 最后仅需判断l[i]+r[i]+b[i]≤F是否成立即可。 l[i]+r[i]+b[i]F

代码:

#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<queue>

#define P pair<int,int>
#define x first
#define y second

using namespace std;

const int N=1e5+10;

int n,m,F;
P calf[N];
int l[N],r[N];
priority_queue<int> heap;

int main()
{
    cin>>n>>m>>F;
    for(int i=1;i<=m;i++) scanf("%d%d",&calf[i].x,&calf[i].y);
    
    sort(calf+1,calf+m+1);
    
    memset(l,0x3f,sizeof l);
    memset(r,0x3f,sizeof r);
    
    //求l[i]
    int sum=0;
    for(int i=1;i<=m;i++) 
    {
        if(heap.size()<n/2) {heap.push(calf[i].y);sum+=calf[i].y;}
        else
        {
            l[i]=sum;
            if(calf[i].y<heap.top())
            {
                sum-=heap.top();
                heap.pop();
                heap.push(calf[i].y);
                sum+=calf[i].y;
            }
        }
    }
    
    //求r[i]
    heap=priority_queue<int>();
    sum=0;
    for(int i=m;i;i--) 
    {
        if(heap.size()<n/2) {heap.push(calf[i].y);sum+=calf[i].y;}
        else
        {
            r[i]=sum;
            if(calf[i].y<heap.top())
            {
                sum-=heap.top();
                heap.pop();
                heap.push(calf[i].y);
                sum+=calf[i].y;
            }
        }
    }
    
    bool flag=true;
    for(int i=m-n/2;i>n/2;i--)
        if(l[i]+calf[i].y+r[i]<=F)
        {
            flag=false;
            cout<<calf[i].x<<endl;
            break;
        }
    
    if(flag) puts("-1");
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值