第十三届 蓝桥杯 Java B组【本科】

一、引言

填空

填空题:只需要算出结果后提交即可;结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

编程

编程题:要求写出完整代码并符合题干中时间内存限制。

二、题目

试题A:星期计算【填空题】

本题总分:5 分
【问题描述】
已知今天是星期六,请问20的22天后是星期几?注意用数字 1 到 7 表示星期一到星期日。

// Kim:
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        int star = 6;

        int m = 0;
        int n = 20;
        for(int i=1;i<=22;i++){
            m = n%7;
            n = n*m;
        }
        int res = (star+m)%7;
        res = (res==0?7:res);
        System.out.println(res);
        scan.close();
    }
}

// 结果:测试通过
// 注意:星期一到星期六可以通过取余得到,星期天要特殊处理

试题B:山【填空题】

本题总分:5 分
【问题描述】
这天小明正在学数数。他突然发现有些正整数的形状像一座“山”,比如 123565321、145541,它们左右对称(回文)且数位上的数字先单调不减,后单调不增。
小明数了很久也没有数完,他想让你告诉他在区间 [2022, 2022222022] 中有多少个数的形状像一座“山”。

// Kim:
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        int count = 0;
        for(int i=2022;i<=2022022;i++){
            if(isHui(i) && isUp(i)){
                count++;
            }
        }
        System.out.println(count);
        scan.close();
    }
        public static boolean isHui(int num){
            String str1 = String.valueOf(num);
    //        System.out.println(str1);
            StringBuffer stf = new StringBuffer(str1);
    //        System.out.println(stf);
            String str2 = stf.reverse().toString();
    //        System.out.println(str2);
            if(str1.equals(str2)){
                return true;
            }
            return false;
        }
        public static boolean isUp(int num){
            char []chars = String.valueOf(num).toCharArray();
            int len = chars.length/2;
    //        System.out.println(len);
            for(int i=1;i<=len;i++){
                if(chars[i]<chars[i-1]){
                    return false;
                }
            }
            return true;
        }
}

// 结果:官网内运行超时、idea中内存溢出错误;基本思路和正确的代码一致、在数据量少的情况运行正确;不知道是不是在String、StringBuffer和char数组之间转换的时候浪费了太多的时间,比参考代码运行时间长一点
// 巩固:int转String方法:valueOf(str)、StringBuffer转String方法:toString()、StringBuffer倒置方法:reverse()、String转char数组方法:toCharArray()

// 参考的正确代码在官网内也运行超时、但在idea中可以显示正确结果;代码如下:

// *
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        int sum = 0;
        for(int i=2022;i<=2022222022;i++) {
            if(isUp(i)&&isMirror(i)) {
                sum++;
            }
        }
        System.out.println(sum);
    }
    public static boolean isMirror(int num){
        StringBuffer str = new StringBuffer(num+"");
        if((str.toString()).equals(str.reverse().toString()))
            return true;
        return false;
    }

    public static boolean isUp(int num){
        String st=num+"";
        int len=st.length()%2==0?st.length()/2:st.length()/2+1;
        for(int i=1;i<len;i++) {
            if(st.charAt(i)<st.charAt(i-1)) {
                return false;
            }
        }
        return true;
    }
}

// 学习:int转换为String的快速方法:num+“”

// 最后找的正确代码如下:

public class Main{
  //[2022,2022222022]
  static int cnt=0,cnt2=0;
  public static void main(String[] args) {
      for (int i =10; i <=20222; i++) {
        if(check(i)) {
          if(i>=22) {
              cnt++;
          }
        }
      }
      System.out.println(cnt+cnt2);
  }
  public static boolean check(int i) {
      String tmp=String.valueOf(i);
      int index=tmp.length()-1;
      while(index>=1) {
          int j=Integer.valueOf(String.valueOf(tmp.charAt(index)));
          int k=Integer.valueOf(String.valueOf(tmp.charAt(index-1)));
          if(j<k) return false;
          index--;
      }
      if(tmp.length()<5) {
          int diff=10-Integer.valueOf(String.valueOf(tmp.charAt(tmp.length()-1)));
          cnt2+=diff;
      }
      return true;
  }
}

// 没看明白循环和cnt2计数的操作

最后决定在给定环境中利用代码*分成多段进行操作并将结果相加得出最终结果

试题C:字符统计【编程题】

时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
给定一个只包含大写字母的字符串 S ,请你输出其中出现次数最多的字母。如果有多个字 母均出现了最多次,按字母表顺序依次输出所有这些字母。
【输入格式】
一个只包含大写字母的字符串 S .
【输出格式】
若干个大写字母,代表答案。
【样例输入】
BABBACAC
【样例输出】
AB
【评测用例规模与约定】
对于 100% 的评测用例,1 ≤ |S | ≤ 10^{6}.

// Kim:
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        String str = scan.next();
        char []chars = str.toCharArray();
        int len = str.length();

        int []count = new int[26];
        for(int i=0;i<len;i++){
            int loc = (char)(chars[i])-65;
            count[loc]++;
        }

        int max = 0;
        int []maxloc = new int[26];
        int maxcnt = 0;
        for(int i=0;i<26;i++){
            if(count[i]>max){
                max = count[i];
            }
        }

        for(int i=0;i<26;i++){
            if(count[i]==max){
                char res = (char)(i+65);
                System.out.print(res);
            }
        }

        scan.close();
    }
}

// 结果:测试通过
// 积累:借助下标来实现计数

试题D:最少刷题数【编程题】

时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
小蓝老师教的编程课有 N 名学生,编号依次是 1 . . . N。第 i 号学生这学期刷题的数量是 A_{i}。
对于每一名学生,请你计算他至少还要再刷多少道题,才能使得全班刷题比他多的学生数不 超过刷题比他少的学生数。
【输入格式】
第一行包含一个正整数 N。
第二行包含 N 个整数:A_{1},A_{2},A_{3} ,. . . , A_{N}.
【输出格式】
输出 N 个整数,依次表示第 1 . . . N 号学生分别至少还要再刷多少道题。
【样例输入】
5
12 10 15 20 6
【样例输出】
0 3 0 0 7
【评测用例规模与约定】
对于 30% 的数据,1 ≤ N ≤ 1000, 0 ≤ A_{i} ≤ 1000.
对于 100% 的数据,1 ≤ N ≤ 100000, 0 ≤ A_{i} ≤ 100000.

// Kim:
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        int n = scan.nextInt();
        int []a = new int[n];
        int []t = new int[n];

        for(int i=0;i<n;i++){
          a[i] = scan.nextInt();
          t[i] = a[i];
        }

        Arrays.sort(t);

        int midloc = n/2;
        int mid = t[midloc];
        int fmid = t[(midloc-1)];

        int []res = new int[n];

        for(int i=0;i<n;i++){
          if(a[i]==mid){
            if(a[i]==fmid){
              res[i] = 1;
            }else{
              res[i] = 0;
            }            
          }          
          
          if(a[i]>mid){
            res[i] = 0;
          }

          if(a[i]<mid){
            if(n%2==0){
              res[i] = mid-a[i];
            }else{
              res[i] = mid-a[i]+1;
            } 
          }

        }

        for(int i=0;i<n;i++){
          System.out.print(res[i]);
          System.out.print(" ");
        }
        scan.close();
    }
}

// 陷入的盲区:每次拿出一个,数量上不会有太大变化,也就是说可实现每次和中位数比较即可
// 测试结果:5/10 错误1eg:0 0 1 1 1 1 1 2 2 2 10->要保证此时坐标和midloc不同
// 在调试好错误1后,调试无问题,但提交检测有问题

试题E:求阶乘【编程题】

时间限制: 1.0s 内存限制: 512.0MB
本题总分:15 分
【问题描述】
满足 N! 的末尾恰好有 K 个 0 的最小的 N 是多少?
如果这样的 N 不存在输出 −1。
【输入格式】
一个整数 K。
【输出格式】
一个整数代表答案。
【样例输入】
2
【样例输出】
10
【评测用例规模与约定】
对于 30% 的数据,1 ≤ K ≤ 10^{6}.
对于 100% 的数据,1 ≤ K ≤ 10^{18}.

// Kim:
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        int k = scan.nextInt();

        double temp = Math.pow(10, k);
        k = (int)temp;
        int k1 = k*10;

        long cul = 1;
        for(long i=2;i<=5;i++){
          cul = cul*i;
        }
        long count = 6;
        while(count<=k){
          if(cul%k==0 && cul%k1!=0){
            System.out.println(count-1);
            return; 
          }
          cul = cul*count;
          count++;
        }
        System.out.println(-1);
        scan.close();
    }
}

// 第一时间想到的暴力法:只通过1个测试用例,其余均出现段错误及运行超时
// 尝试二分类去解决:没想出来

最后找的正确代码如下:

public class Main {
    
    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        // 末位0的个数
        long k = scanner.nextLong();
        
        long lef = 1;
        long rig = (long)9e18;
        
        // 找符合条件的最小值
        // 采用二分查找法
        while(lef < rig) {
            long mid = (lef + rig)/2;
            
            if(k <= num_of_zero(mid)) {
                rig = mid;
            }
            else {
                lef = mid + 1;
            }
                    
        }
        // while最终停止情况如下两种:
        // 一是当rig恰好取得结果值即k=num_of_zero(mid)【即找到】,在下一次循环时进入else,lef=rig+1,即lef>rig跳出循环;
        // 二是当k>num_of_zero(mid)【即未找到】,在下一次循环时进入else,lef=rig+1,即lef>rig跳出循环
        if(num_of_zero(lef) == k)
            System.out.println(rig);
        else {
            System.out.println(-1);
        }
        
        scanner.close();
            
    }

    // 判断一个数的阶乘的末尾有几个0
    private static Long num_of_zero(long tmp) {
        // cnt标记0的个数
        long cnt = 0;
        while(tmp > 0) {
            cnt += tmp / 5;
            tmp /= 5;
        }
        
        return cnt;
    }

}

学习:num_of_zero()判断一个数的阶乘的末尾有几个0方法:能产生0的质数组合只有2 * 5,然后问题就转变成了对N!质数分解后,一共有几个5,因为2的个数显然多于5【偶数均可以分解出2】。比如计算25!的末尾0的个数,包含5的数有5,10,15,20,25,其中25中包含两个5【25=5*5】,所以一共包含6个5,25!的末尾有6个0

试题F:最大子矩阵【编程题】

时间限制 : 1.0s
内存限制 : 512.0MB
本题总分: 15 分
【问题描述】
小明有一个大小为 N × M 的矩阵,可以理解为一个 N 行 M 列的二维数组。
我们定义一个矩阵 m 的稳定度 f ( m ) 为 f ( m ) = max ( m ) − min ( m ) ,其中 max ( m )
表示矩阵 m 中的最大值, min ( m ) 表示矩阵 m 中的最小值。现在小明想要从这
个矩阵中找到一个稳定度不大于 limit 的子矩阵,同时他还希望这个子矩阵的面
积越大越好(面积可以理解为矩阵中元素个数)。
子矩阵定义如下:从原矩阵中选择一组连续的行和一组连续的列,这些行
列交点上的元素组成的矩阵即为一个子矩阵。
【输入格式】
第一行输入两个整数 N , M ,表示矩阵的大小。
接下来 N 行,每行输入 M 个整数,表示这个矩阵。
最后一行输入一个整数 limit ,表示限制。
【输出格式】
输出一个整数,分别表示小明选择的子矩阵的最大面积。
【样例输入】
3 4
2 0 7 9
0 6 9 7
8 4 6 4
8
【样例输出】
6
【样例说明】
满足稳定度不大于 8 的且面积最大的子矩阵总共有三个,他们的面积都是
6 (粗体表示子矩阵元素):
2 0 7 9
0 6 9 7
8 4 6 4
2 0 7 9
0 6 9 7
8 4 6 4
2 0 7 9
0 6 9 7
8 4 6 4
【评测用例规模与约定】
评测用例编号 N M
1, 2 1 ≤ N ≤ 10 1 ≤ M ≤ 10
3, 4 N = 1 M ≤ 100000
5 ∼ 12 1 ≤ N ≤ 10 M ≤ 10000
13 ∼ 20 1 ≤ N ≤ 80 1 ≤ M ≤ 80
对于所有评测用例, 0 ≤ 矩阵元素值 , limit ≤ 10^{5} 。

思路:二维矩阵比较大小的时候转换为一维矩阵去比较

import java.io.*;
import java.util.*;

public class Main {
    // max[k][i][j]表示第k列中[i,j]之间的最大值->(1)
    // 将每一段矩阵高的最大值和最小值存储下来并保证寻找limit内最大
    static int[][][] max;
    static int[][][] min;
    static int n, m, limit,ans;
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
        String[] s=br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        m = Integer.parseInt(s[1]);
        // 坐标:列 行 行
        max=new int[m+1][n+1][n+1];
        min=new int[m+1][n+1][n+1];
        // 矩阵赋值
        for (int i = 1; i <= n; i++) {
            s=br.readLine().split(" ");
            for (int j = 1; j <= m; j++) {
                max[j][i][i] = min[j][i][i] = Integer.parseInt(s[j-1]);
            }
        }
        // 表示最大限制
        limit = Integer.parseInt(br.readLine());
        //预处理  复杂度 n^2*m
        // 这里的i和j表示的是行的范围,即[k][j][j]表示的就是某行某列的元素
        // 记录列中每一段范围的最大值和最小值
        for (int k = 1; k <= m; ++k) {
            for (int i = 1; i <= n; ++i) {
                for (int j = i + 1; j <= n; ++j) {
                    max[k][i][j] = Math.max(max[k][i][j - 1], max[k][j][j]);
                    min[k][i][j] = Math.min(min[k][i][j - 1], min[k][j][j]);
                }
            }
        }
        
        for (int x1 = 1; x1 <= n; x1++) {
            for (int x2 = x1; x2 <= n; x2++) {
                int l = 1, r = m;
                while (l < r) {
                		 // >>右移运算符 右移一位相当于除以2 
                    int mid = l + r + 1 >> 1;
                    if (check(x1, x2, mid)) l = mid;
                    else r = mid - 1;
                }
                if (check(x1,x2,r)) ans=Math.max(ans,(x2-x1+1)*r);
            }
        }
        out.println(ans);
        out.flush();
    }

    //k是窗口大小
    static boolean check(int x1, int x2, int k) {
        // Deque表示双端队列,双端队列是在两端都可以进行插入和删除的队列
        Deque<Integer> qmax = new ArrayDeque<>();
        Deque<Integer> qmin = new ArrayDeque<>();
        for (int i = 1; i <= m; i++) {
            // peekLast()返回双端队列最后一个元素,若元素为空,则返回null
            // pollLast()此方法检索并删除此双端队列的最后一个元素,如果此双端队列为空,则返回null
            while (!qmin.isEmpty() && min[qmin.peekLast()][x1][x2] > min[i][x1][x2]) qmin.pollLast();
            // offerLast(E e)方法会将指定的元素插入Deque的末尾
            qmin.offerLast(i);
            //处理最大
            // peekFirst() 方法检索而不是删除给定双端队列的第一个元素
            // pollFirst() 此方法检索并删除此双端队列的第一个元素,如果此双端队列为空,则返回null
            if (!qmax.isEmpty() && qmax.peekFirst() < i - k + 1) qmax.pollFirst();
            while (!qmax.isEmpty() && max[qmax.peekLast()][x1][x2] < max[i][x1][x2]) qmax.pollLast();
            // offerLast(E e)方法会将指定的元素插入Deque的末尾
            qmax.offerLast(i);
            //说明窗口为k
            if (i >= k && max[qmax.peekFirst()][x1][x2] - min[qmin.peekFirst()][x1][x2] <= limit) return true;
        }
        return false;
    }
}

题解:https://blog.51cto.com/u_15492302/5729136#3Ac_code_47
(1)确定x边界【即在数组中记录列(矩阵高)每一范围的最大值及最小值】-二维处理为一维
(2)确定y边界【即确定矩阵宽】-二分
(3)定义全局变量计算面积,更新答案

试题G:数组切分【编程题】

试题H:回忆迷宫【编程题】

试题I:红绿灯【编程题】

试题J:拉箱子【编程题】

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值