暑假集訓Day 5 P3963 [TJOI2013] 獎學金

題目大意

豬仙發現人類能夠上不少大學,而豬們卻沒有大學可上。爲了解決這個問題,因而他創立了一所大學,取名爲豬仙大學。
爲了選拔合適的學生入學,他發明了一種學術能力測試(簡稱\(CSAT\)),這種測試的分數異常精確,每頭豬的成績能夠用1到2,000,000,000之間的一個整數表示。
豬仙大學的學費很貴(豬仙比較黑),不少豬都負擔不起,他們須要申請一些獎學金(\(1=<獎學金<=10,000\))。但是政府並無爲豬準備獎學金,因而全部的預算都必需要從學校自身有限的基金中間扣除(設基金總額爲F,\(0=<F<=2,000,000,000\))。
更槽的事,豬仙大學的只有N(\(1=<N<=19,999\))間教室,N是一個奇數,而一共有C頭豬申請入學,爲了讓最多的豬接受教育,豬仙打算接受N頭豬的申請,並且她還想讓這些豬
成績的中位數儘量地高。
給定每頭豬的\(CSAT\)成績和打算申請的獎學金數目,以及能夠資助的基金總數,肯定豬仙接受哪些豬的申請纔可使成績的中位數達到最大。node

輸入格式

  • 第一行:三個用空格分開的整數:N,C 和F。
  • 第二行到C+1行:每行有兩個用空格分開的整數。第一個數是這頭豬的 \(CSAT\)成績,第二個數是這頭豬想申請的獎學金。

輸出格式

第一行:一個整數,表示豬仙能夠獲得的最大中位數,若是現有基金不夠資助N頭豬,則輸出-1。

c++

樣例

樣例輸入

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

樣例輸出

35

算法分析

  • 數據範圍高達100,000 顯然至少須要一個O(n\(log_n\))的算法
  • 已經知道要招入n只豬 所以中位數i知足 \(\frac n2\)+1≤i≤C−\(\frac n2\)
  • 若是i=\(\frac n2\)+1 那麼成績前\(\frac n2\) 只豬都要選上
  • 所以能夠枚舉中位數,用一個大根堆維護獎金,枚舉一箇中位數若是當前的獎金比以前的大根堆堆頂小,則交換,始終保持大根堆有\(\frac n2\)個元素,同時用f[i]來維護若是用i作中位數,前\(\frac n2\)個數的最小獎金
  • 一樣,再倒序枚舉一次,用g[i]表示若是用i作中位數,後\(\frac n2\)個數的最小獎金
  • 這樣咱們再遍歷一次i 而後求出最小的f[i]+g[i]+a[i].w 對應的最大的a[i].s就是咱們要的答案了

算法展現

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int n,c,F,ans;
int f[maxn],g[maxn],sum;

priority_queue <int> q;//大根堆

struct node{
	int s,w;//s爲成績 w爲獎金
}a[maxn];

int cmp(node a,node b){return a.s < b.s;}

int main(){
	scanf("%d%d%d",&n,&c,&F);
	for(int i = 1;i <= c;++i)scanf("%d%d",&a[i].s,&a[i].w);
	sort(a+1,a+1+c,cmp);//按成績升序排列
	for(int i = 1;i <= n/2;++i){
		sum += a[i].w;//計算成績最低的前n/2個數的獎金和
		q.push(a[i].w);//進堆
	}
	for(int i = n/2+1;i <= c;++i){
		f[i] = sum;//以i爲中位數 前n/2個數的最小獎金數
		int top = q.top();
		if(top > a[i].w){//若是當前元素的獎金數小於堆頂
			q.pop();
			sum -= top;
			sum += a[i].w;
			q.push(a[i].w);//替換掉堆頂並更新下個f[i]的值
		}
	}
	sum = 0;
	while(!q.empty()) q.pop();
	for(int i = c;i >= c-n/2+1;--i){//同上 
		sum += a[i].w;//計算成績最高的前n/2個數的獎金和
		q.push(a[i].w);//進堆
	}
	for(int i = c-n/2;i >= 1;--i){
		g[i] = sum;//以i爲中位數後n/2個數的最小獎金數
		int top = q.top();
		if(top > a[i].w){//若是當前元素的獎金數小於堆頂
			q.pop();
			sum -= top;
			sum += a[i].w;
			q.push(a[i].w);//替換掉堆頂元素並更新下個g[i]的值
		}
	}
	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 0;
		}
	printf("-1\n");//遍歷過每個中位數了
	return 0;
}
相關文章
相關標籤/搜索