TJOI2013 奖学金—大根堆实现(洛谷P3963)

奖学金

题目描述

小张学院有 \(c\) 名学生,第 \(i\) 名学生的成绩为 \(ai\) ​,要获得的奖学金金额为 \(bi\)
要从这 \(c\) 名学生中挑出 \(n\) 名学生发奖学金。这个神秘人物爱好奇特,他希望得到奖学金的同学的成绩的中位数尽可能大,但同时,他们的奖学金总额不能超过 \(f\)

输入格式

第一行有三个整数,分别表示要挑出的学生人数 \(n\) ,学生总人数 \(c\) 和奖学金总额的最大值 \(f\)
\(2\) 到第 \((c+1)\) 行,每行两个整数,第 \((i+1)\) 行的整数依次表示第 \(i\) 名学生的成绩 \(ai\)​ 和如果要给他发奖学金,则需要发的金额数 \(bi\) ​。

输出格式

输出一行一个整数表示答案。如果无法满足神秘人的条件,请输出 \(−1\)

输入输出样例

输入 #1

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

输出 #1

35

输入 #2

5 6 9
4 0
4 1
6 3
8 0
10 4
10 5

输出 #2

6

说明/提示

样例 1 解释
选择成绩为 \(5\)\(35\)\(50\) 的三名同学,奖金总额为 \(18+30+21=69\)

数据规模与约定

对于 30% 的数据,保证 n≤10^3,c≤2×10^3。
对于 100% 的数据,保证 3≤n≤10^5,n≤c≤2×10^5,0≤f≤2×10^9,0≤ai≤2×10^9,0≤bi≤10^5。

大致思路

首先要保证这道题要求中位数的最大值,那么就是尽可能的放成绩尽可能最高的那个,首先用\(sort\)根据成绩来排序,一个堆把前\((n+1)/2\)的数放进大跟堆里,然后从\((n+1)/2\)开始\(for\)循环来一次枚举成绩高的,要是成绩高而且奖学金需求还比大跟堆里最大的金额少,那么一定要放进去,并把价格最高的踢出来,保证刚放进去的数是第\((n+1)/2\)个数,直到搜到第\(c-(n+1)/2\)的时候,停止,维护了当前形式的最大中位数,再从后\((n+1)/2\)个数里向前走,求出当前形式的最小中位数,然后跑一个\(for\)循环来求出最大的中位数即可。

代码实现

#include 
const int maxn=2e5+5;
int n,c,F;
std::priority_queue  q;
struct Node{
    int s,w;//分数,奖金
} a[maxn];
bool cmp(const Node &a, const Node &b){
    return a.sa[i].w){//如果当前的奖金小于堆顶则交换掉
            q.pop();
            sum-=top;
            sum+=a[i].w;
            q.push(a[i].w);
        }
    }

    sum=0;
    while(!q.empty()) q.pop();
    for(int i=c;i>=c-n/2+1;--i){//成绩最高的n/2进入队列
        sum+=a[i].w;
        q.push(a[i].w);
    }
    //g[i]:表示以i为中位数后n/2人的最低奖金
    for(int i=c-n/2;i>=1;--i){
        g[i]=sum;
        int top=q.top();
        if(top>a[i].w){//交换
            q.pop();
            sum-=top;
            sum+=a[i].w;
            q.push(a[i].w);
        }
    }
    //中位数的取值范围是[n/2+1,c-n/2]
    //因为要求最大中位数,所以倒序
    for(int i=c-n/2;i>=n/2+1;--i)
        if(a[i].w+f[i]+g[i]<=F){
            printf("%d", a[i].s);
            return;
        }
    printf("-1\n");
}
int main(){
    Init();
    Solve();
    return 0;
}

代码是教练员的,自己码风有一..恶心

你可能感兴趣的:(TJOI2013 奖学金—大根堆实现(洛谷P3963))