Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 6572 | Accepted: 1923 |
Description
Input
Output
Sample Input
3 5 70
30 25
50 21
20 20
5 18
35 30
Sample Output
35
Hint
题意:奶牛学校招生,要录取n(奇数)个学生,报名了c个学生,每个学生有成绩s_i和需要的补助金f_i。学校的补助金一共为F元,挑选n个学生,f_1+f_2+f_i+....+f_n不得大于n,且n个奶牛的s_i的中位数最大,输出这个数?
题解:
优先队列+枚举,分治解法:
将奶牛按照成绩s_i从小到大排序,从 n/2 到 m-1-n/2枚举中位数,记录每个i的前面n/2数的f之和,和后面n/2数的f之后,然后枚举满足条件最大的中位数。
代码如下:
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; struct node { int s,f; bool operator <(const node &x) const { return f<x.f; } }cow[100010]; int minl[100000],minr[100000],n,m,F,suml,sumr; //minl[i]表示i左边前n/2小的数的和,minr[i]表示i右边前n/2小的数的和 int cmp(node a,node b) { return a.s<b.s; } int main() { int i,j; while(scanf("%d%d%d",&n,&m,&F)!=EOF) { for(i=0;i<m;++i) scanf("%d%d",&cow[i].s,&cow[i].f); sort(cow,cow+m,cmp); suml=0; sumr=0; priority_queue<node>ql,qr;//优先f大的元素先出队列 for(i=0;i<m;++i)//查找对于每个i左边n/2个元素的最小和 { if(i<n/2) { ql.push(cow[i]); suml+=cow[i].f; continue; } minl[i]=suml; if(cow[i].f<ql.top().f) { suml-=ql.top().f; ql.pop(); suml+=cow[i].f; ql.push(cow[i]); } } for(i=m-1;i>=0;--i)//查找对于每个i右边n/2个元素的最小和 { if(i>m-1-n/2) { qr.push(cow[i]); sumr+=cow[i].f; continue; } minr[i]=sumr; if(cow[i].f<qr.top().f) { sumr-=qr.top().f; qr.pop(); sumr+=cow[i].f; qr.push(cow[i]); } } int ans=-1; for(i=m-1-n/2;i>=n/2;--i)//从大到小枚举满足条件的cow[i].s { if(minl[i]+minr[i]+cow[i].f<=F) { ans=cow[i].s; break; } } printf("%d\n",ans); } return 0; }
二分搜索—最大化最小值解法:
分别用分数和所需补助金给奶牛排序,用分数为界限将奶牛分为左右两区间两部分,再分别查找左区间满足条件的奶牛数 left 和右区间满足条件的奶牛数 right。 讨论 left 和 right 的值分情况取讨论如何修改下一次要查找的值。
具体内容见代码和注释:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct node { int s,f,id; }cows[100010],cowf[100010]; int n,m,F; int cmps(node a,node b) { return a.s<b.s; } int cmpf(node a,node b) { return a.f<b.f; } int dix(int x) { int i,sum=cows[x].f,left=0,right=0; for(i=0;i<m;++i) { if(cowf[i].id<x&&(sum+cowf[i].f<=F)&&(left<n/2)) { sum+=cowf[i].f; left++; } else if(cowf[i].id>x&&(sum+cowf[i].f<=F)&&(right<n/2)) { sum+=cowf[i].f; right++; } }//以下是取当前mid值的方案可能出现四种情况 if((left<n/2)&&(right<n/2))//不可能满足条件,直接输出-1 return -1; else if(left<n/2)//不满足条件,但可能将mid值调大就满足条件了 return 1; else if(right<n/2)//不满足条件,但可能将mid值调小就满足条件了 return 2; return 0;//满足条件,但mid的值可能可以更大 } int main() { int i,left,right,ans,mid; while(scanf("%d%d%d",&n,&m,&F)!=EOF) { for(i=0;i<m;++i) scanf("%d%d",&cows[i].s,&cows[i].f); sort(cows,cows+m,cmps); for(i=0;i<m;++i) cows[i].id=i; memcpy(cowf,cows,sizeof(cows));//内存拷贝函数,把cows[]拷贝给cowf[] sort(cowf,cowf+m,cmpf); left=0; right=m; ans=-1;//注意ans初始化为-1可以省去后面的特判,没合适方案输出-1 while(left<=right) { mid=(left+right)>>1; switch(dix(mid)) { case -1:{ printf("-1\n"); return 0; } case 0:{ ans=cows[mid].s; left=++mid; break; } case 1:{ left=++mid; break; } case 2:{ right=--mid; } } } printf("%d\n",ans); } return 0; }