2022年第十三届蓝桥杯Java省赛B组 试题D:最少刷题数(AC)

蓝桥云课 最少刷题数评测

试题内容

题目以及样例解释

我们先来分析一下题目,看需要解决什么样的问题~

题目中的解题重点句在于: 比他刷题多的同学不超过比他刷题少的同学

也就是说对于每一位同学我们要找到刷题比他多的同学和刷题比他少的同学

接下来对样例进行一下解释~方面大家更好的理解题目含义

第一位同学:12道题 比他多:15、20 ;比他少:6、10 无需再刷题

第二位同学:10道题 比他多:12、15、20;比他少:6 需要刷3道题

注意一下这里 如果刷两道题 那么比他多的是2道比他少的是一道题还是不符合题意,需要再多刷一道题

第三位同学:15道题 比他多:20; 比他少:6、10、12 无需再刷题

第四位同学:20道题 比他多:无; 比他少:6、10、12、15 无需再刷题

第五位同学:6道题 没有比他更少的 根据第二位同学 应该刷7题

思路分析

按照我们常用的思路~

要找到几个同学刷题数量的中间值,每位同学和中间值去比较判断,如果刷题数和人数符合要求,无需再刷题就可以;如果不符合就要刷题到中间数+1才可符合题意;中间值的求法与人数的奇偶性有关,这还需要分类讨论。那么如果几个同学刷题数量一样如:3 10 10 12 14 求法又需要去单独判断 是不是特别复杂!!!(看过别人的题解~中值判断的做法都是相当复杂,有些还存在着些许问题,甚至我改完以后好不容易没有瑕疵还被卡超时了😭)

下面我们重新来分析一下这道题目,看看有没有其他的思路和方法

对于每一个学生而言,我们都需要记录刷题数目比它多的学生和刷题数目比他少的学生,那么我们是不是就可以开一个数组记录下刷每道题的人数有多少。那么我们是不是就可以得到比某一道题目多或者少的同学的数量呢? 这里是不是就可以想到前缀和数组,这样就可以在O(1)的时间复杂度之下得到任意区间内的刷题学生的数量,这样就解决了我们开始需要查询的问题

接下来,我们继续分析:如果我们用cnt[]数组记录前缀和来表示学生刷题的数量,那么我们假设现在有一个学生的刷题数量为x,那么刷题数量比他少的同学就是cnt[x-1],刷题数两比他多的同学就是cnt[N]-cnt[x],刷题数目一样多的包括自己在内就记作cnt[x]。

随着刷题数量的增加,比我刷题少的同学数量在增多,比我刷题多的同学数量在减少,当我们达到了某一个临界条件时,无论你再刷多少题目都是符合题意的。也就是说我们题目中的最少刷题数就是找到一个a到正无穷区间左侧临界的最小值。由于这个过程符合二段性的特点,我们自然可以想到二分答案的算法来解决! 所以这道题目就是利用前缀和+二分的思想来解决题目。(利用快读快写加快运行速度)

AC代码(Java实现)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class B组真题最少刷题数 {
    static int N=100010;
    static int[] a=new int[N];
    //cnt[i]表示刷了i道题目的人数
    static int[] cnt=new int[N];
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out=new PrintWriter(new PrintWriter(System.out));
    public static void main(String[] args) throws IOException {
        int n=Integer.parseInt(br.readLine());
        String[] s=br.readLine().split(" ");
        for (int i = 0; i <n ; i++) {
            a[i]=Integer.parseInt(s[i]);
            cnt[a[i]]++;
        }
        for (int i = 1; i <=100000 ; i++) {
            cnt[i]+=cnt[i-1];
        }
        for (int i = 0; i < n; i++) {
        //如果比他刷题少多的学生本来就没有超过比他刷题数目少的学生,就无需再刷题了
            if(cnt[100000]-cnt[a[i]]<=cnt[Math.max(0,a[i]-1)]){
                out.print(0+" ");
                continue;
            }
            int l=a[i]+1,r=100000;
            //利用二分找到我们应该刷多少题目合适
            while (l<r){
                int mid=l+r>>1;
                if (cnt[100000]-cnt[mid]<=cnt[mid-1]-1){
                    r=mid;
                }else {
                    l=mid+1;
                }
            }
            out.print(r-a[i]+" ");
        }
        out.flush();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值