上链接:https://www.luogu.com.cn/problem/P7072
上题干:
题目描述
NOI2130 即将举行。为了增加观赏性,CCF 决定逐一评出每个选手的成绩,并直播即时的获奖分数线。本次竞赛的获奖率为w%,即当前排名前 w% 的选手的最低成绩就是即时的分数线。
更具体地,若当前已评出了 p 个选手的成绩,则当前计划获奖人数为max(1,⌊p×w%⌋),其中 w 是获奖百分比,⌊x⌋ 表示对 x 向下取整,max(x,y) 表示 x 和 y 中较大的数。如有选手成绩相同,则所有成绩并列的选手都能获奖,因此实际获奖人数可能比计划中多。
作为评测组的技术人员,请你帮 CCF 写一个直播程序。
输入格式
第一行有两个整数 n,w。分别代表选手总数与获奖率。
第二行有 n 个整数,依次代表逐一评出的选手成绩。输出格式
只有一行,包含 n 个非负整数,依次代表选手成绩逐一评出后,即时的获奖分数线。相邻两个整数间用一个空格分隔。
这道题是一个模拟题,那么我们就按照题意来模拟。
我们按照它给的样例来,
n=10,w=60;
当输入第一个人的成绩:200,排下序:200 ,更新一下当前获奖人数:max(1,1*0.6)=1
即时获奖分数线:200
当输入第二个人成绩:300 ,排一下序:300 ,200 更新一下当前获奖人数:max(1,2*0.6)=1
即时获奖分数线:300
当输入第三个人成绩:400 ,排一下序:400,300 ,200 更新一下当前获奖人数:max(1,3*0.6)=1
即时获奖分数线:400
当输入第四个人成绩:500 ,排一下序:500,400,300 ,200 更新一下当前获奖人数:max(1,4*0.6)=2
即时获奖分数线:400
当输入第五个人的成绩:600,排序:600 500 400 300 200 更新一下当前获奖人数:max=(1,5*0.6)=3
即时获奖分数线:400
…………
所以,我们需要的操作就是:1,循环读入第i个人的成绩。2,每次把当前拥有的人的成绩排序,更新获奖人数,根据获奖人数,从最大成绩开始往下扫描获奖人数个单位,这个扫描到的成绩就是即时获奖分数线,我们此时将它输出即可。
但是我们会发现,如果用快排,sort排序,等排序,会超时。
并且,我们的数据有一个特点:就是这个数列的值域很小,【0,600】
所以我们可以用 我们的经典排序————桶排序(计数排序)来达到空间换时间的效果。
我们只需要准备0~600 这么601个桶,用桶的下标来储存成绩,桶装这个成绩的人数。
然后就不需要再特地排序了,因为我们的即时获奖分数线是从最高的分数,往下数(计划获奖人数)个单位。
我们只需要把序号高的桶放在第一位,用sum变量累加从最高分开始的人数,如果这个人数,超过了计划人数,那么此时的分数就是获奖分数线。
上代码:
const int N = 1e5 + 10;
int n, w;
int a[N];
int box[N];
int main()
{
cin >> n >> w;
for(int i=1;i<=n;i++)
{
cin >> a[i];
box[a[i]]++;
int sum = 0;
for (int j = 600; j >= 0; j--)
{
sum += box[j];
int plan = max(1,(int) (i * w / 100.0));
if (sum >= plan)
{
cout << j<<' ';
break;
}
}
}
}