第十三届蓝桥杯省赛Java B组真题及题解

时间:2022-02-03 01:15:03

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

本题总分: 5 分

【问题描述】

已知今天是星期六,请问 2022 22 天后是星期几?
注意用数字 1 到 7 表示星期一到星期日。

【答案提交】

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

【题解】

import java.time.DayOfWeek;
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        // 获取今天的日期
        LocalDate today = LocalDate.now();
        
        // 计算22天后的日期
        LocalDate futureDate = today.plusDays(22);
        
        // 计算22天后是星期几
        DayOfWeek futureDayOfWeek = futureDate.getDayOfWeek();
        
        // 输出结果
        System.out.println("今天是星期" + today.getDayOfWeek().getValue());
        System.out.println("22天后是星期" + futureDayOfWeek.getValue());
    }
}

【答案】

7

B试题 : 山【填空题】

本题总分: 5 分

【问题描述】

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

【答案提交】

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

【题解】

public class Main {
    public static void main(String[] args) {
        int count = 0; // 计数器,记录符合条件的数字个数
        for (int i = 2; i <= 2022222022; i++) { // 枚举数字
            if (isMountain(i)) { // 判断是否符合条件
                count++; // 计数器加一
            }
        }
        System.out.println(count); // 输出结果
    }

    // 判断一个数字是否符合条件
    public static boolean isMountain(int n) {
        String s = String.valueOf(n); // 将数字转换为字符串
        int len = s.length(); // 数字的长度
        if (len % 2 == 0) { // 如果数字的长度为偶数,则不符合条件
            return false;
        }
        int mid = len / 2; // 山顶的位置
        int i = mid - 1; // 左边数字的位置
        int j = mid + 1; // 右边数字的位置
        while (i >= 0 && s.charAt(i) <= s.charAt(i + 1)) { // 判断左边数字是否单调不减
            i--;
        }
        while (j < len && s.charAt(j) <= s.charAt(j - 1)) { // 判断右边数字是否单调不增
            j++;
        }
        return i < 0 && j == len; // 如果左边数字单调不减且右边数字单调不增,则符合条件
    }
}

【答案】

3138

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 10 分

【问题描述】

给定一个只包含大写字母的字符串S,请你输出其中出现次数最多的字母。如果有多个字母均出现了最多次,按字母表顺序依次输出所有这些字母。

【输入格式】: 一个只包含大写字母的字符串S

【输出格式】若干个大写字母,代表答案。

【样例输入】BABBACAC

【样例输出】AB

【评测用例规模与约定】

对于 100 %的评测用例, 1 ≤|S|≤106

【题解】

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int[] cnt = new int[26]; // 记录每个字母出现的次数
        int max = 0; // 出现次数最多的字母出现的次数
        for (int i = 0; i < s.length(); i++) {
            cnt[s.charAt(i) - 'A']++; // 统计每个字母出现的次数
            max = Math.max(max, cnt[s.charAt(i) - 'A']); // 更新出现次数最多的字母出现的次数
        }
        for (int i = 0; i < 26; i++) {
            if (cnt[i] == max) { // 如果出现次数等于最大值
                System.out.print((char) (i + 'A')); // 输出对应的字母
                max = -1; // 将 max 设为 -1,表示已经输出了一个出现次数最多的字母
            }
        }
    }
}

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 10 分

【问题描述】

小蓝老师教的编程课有N名学生,编号依次是 1…N。第i号学生这学期
刷题的数量是Ai

对于每一名学生,请你计算他至少还要再刷多少道题,才能使得全班刷题
比他多的学生数不超过刷题比他少的学生数。

【输入格式】:

第一行包含一个正整数N,第二行包含N个整数:A1,A2,A3…A4

【输出格式】输出N个整数,依次表示第 1…N号学生分别至少还要再刷多少道题。

【样例输入】

5
12 10 15 20 6

【样例输出】0 3 0 0 7

【评测用例规模与约定】

对于 30 %的数据, 1≤ N≤ 1000,0 ≤Ai≤ 1000 ;
对于 100 %的数据, 1≤ N≤ 100000,0 ≤Ai≤ 1000 ;

【题解】

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        int[] b = Arrays.copyOf(a, n); // 复制一份原数组
        Arrays.sort(b); // 将复制的数组进行排序
        for (int i = 0; i < n; i++) {
            int rank = Arrays.binarySearch(b, a[i]); // 查找学生i的成绩在排序后的数组中的排名
            int left = rank; // 左侧的学生个数
            int right = n - rank - 1; // 右侧的学生个数
            if (left <= right) { // 如果左侧的学生个数小于等于右侧的学生个数
                System.out.print(0 + " "); // 则不需要再刷题了
            } else { // 否则需要再刷题
                System.out.print(left - right + " ");
            }
        }
    }
}

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 15 分

【问题描述】

满足N!的末尾恰好有K个 0 的最小的N是多少?
如果这样的N不存在输出-1 。

【输入格式】:

一个整数K。

【输出格式】

一个整数代表答案。

【样例输入】

2

【样例输出】

10

【评测用例规模与约定】

对于 30 %的数据, 1≤ K≤ 106 ;
对于 100 %的数据, 1≤ K≤ 1018;

【题解】

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long k = scanner.nextLong();
        long left = 0, right = Long.MAX_VALUE;
        while (left < right) { // 二分查找
            long mid = left + (right - left) / 2;
            if (count(mid) >= k) { // 如果mid的阶乘末尾0的个数大于等于k,则继续在左半边查找
                right = mid;
            } else { // 否则在右半边查找
                left = mid + 1;
            }
        }
        if (count(left) == k) { // 找到了
            System.out.println(left);
        } else { // 找不到
            System.out.println(-1);
        }
    }

    // 计算n!末尾0的个数
    private static long count(long n) {
        long res = 0;
        while (n > 0) {
            n /= 5; // 每隔5个数会出现一个因子5,每隔25个数会出现两个因子5,以此类推
            res += n; // 将所有因子5的个数累加起来
        }
        return res;
    }
}

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 15 分

【问题描述】

小明有一个大小为N× \times×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

【评测用例规模与约定】

对于 30 %的数据, 1≤ N≤ 1000,0 ≤Ai≤ 1000 ;
对于 100 %的数据, 1≤ N≤ 100000,0 ≤Ai≤ 1000 ;

【样例说明】

满足稳定度不大于 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

【题解】

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                a[i][j] = scanner.nextInt();
            }
        }
        int limit = scanner.nextInt();
        int res = 0;
        for (int i = 0; i < n; i++) {
            int[] b = new int[m];
            for (int j = i; j < n; j++) {
                for (int k = 0; k < m; k++) {
                    b[k] += a[j][k]; // 将第i~j行的元素累加到数组b中
                }
                res = Math.max(res, getMaxArea(b, limit)); // 更新最大面积
            }
        }
        System.out.println(res);
    }

    // 获取数组b中的最大面积不大于limit的子数组的面积
    private static int getMaxArea(int[] b, int limit) {
        int res = 0;
        Deque<Integer> stack = new ArrayDeque<>(); // 单调递增栈
        for (int i = 0; i < b.length; i++) {
            while (!stack.isEmpty() && b[i] < b[stack.peek()]) {
                int h = b[stack.pop()]; // 弹出栈顶元素
                int w = stack.isEmpty() ? i : i - stack.peek() - 1; // 计算宽度
                if (h <= limit) { // 如果高度不大于limit,则更新最大值
                    res = Math.max(res, h * w);
                }
            }
            stack.push(i); // 将当前元素入栈
        }
        while (!stack.isEmpty()) { // 处理剩余元素
            int h = b[stack.pop()];
            int w = stack.isEmpty() ? b.length : b.length - stack.peek() - 1;
            if (h <= limit) {
                res = Math.max(res, h * w);
            }
        }
        return res;
    }
}

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 20 分

【问题描述】

已知一个长度为N的数组:A1,A2,A3…AN恰好是 1~N的一个排列。现
在要求你将A数组切分成若干个(最少一个,最多N个)连续的子数组,并且
每个子数组中包含的整数恰好可以组成一段连续的自然数。

例如对于A={1,3,2,4},一共有 5 种切分方法:

1324: 每个单独的数显然是 (长度为 1 的) 一段连续的自然数。

{1}{3,2}{4}:{3,2}{1}{3,2}{4}:{3,2} 包含 2 到 3 , 是 一段连续的自然数, 另外 1 和 4 显然 也是。

{1}{3,2,4}:{3,2,4}{1}{3,2,4}:{3,2,4} 包含 2 到 4 , 是一段连续的自然数, 另外 11 显然也是。

{1,3,2}{4}:{1,3,2}{1,3,2}{4}:{1,3,2} 包含 1 到 3 , 是 一段连续的自然数, 另外 44 显然也是。

{1,3,2,4}{1,3,2,4} : 只有一个子数组, 包含 1 到 4 , 是 一段连续的自然数。

【输入格式】:

第一行包含一个整数N。第二行包含N个整数,代表A数组。

【输出格式】

输出一个整数表示答案。由于答案可能很大,所以输出其对 1000000007 取模后的值

【样例输入】

4
1 3 2 4

【样例输出】

5

【评测用例规模与约定】

对于 30 %评测用例, 1≤ N≤ 20.
对于 100 %评测用例, 1 ≤ N≤ 10000 .

【题解】

import java.util.Scanner;

public class Main {
    static int N = 10010, MOD = 1000000007;
    static int n;
    static int[] w = new int[N];
    static int[][] f = new int[N][N];

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        for (int i = 1; i <= n; i++)
            w[i] = scan.nextInt();
        // f[i][j]表示区间[i,j]能否划分成若干个连续的子区间
        // f[i][j] = 0 表示不能划分,f[i][j] = 1 表示可以划分
        // 初始化f[i][i] = 1,即单独的一个数一定是连续的自然数
        for (int i = 1; i <= n; i++) {
            f[i][i] = 1;
            for (int j = i + 1; j <= n; j++) {
                int len = j - i + 1;
                // 判断区间[i,j]是否能够划分成若干个连续的子区间
                if (check(i, j)) {
                    f[i][j] = 1;
                    // 枚举区间[i,j]的分界点k,使得f[i][j] = f[i][k] * f[k+1][j]
                    for (int k = i; k < j; k++) {
                        f[i][j] = (f[i][j] + (long) f[i][k] * f[k + 1][j]) % MOD;
                    }
                }
            }
        }
        System.out.println(f[1][n]);
    }

    // 判断区间[l,r]是否能够划分成若干个连续的子区间
    private static boolean check(int l, int r) {
        int len = r - l + 1;
        int[] q = new int[len + 1];
        for (int i = l; i <= r; i++) {
            q[i - l + 1] = w[i];
        }
        Arrays.sort(q, 1, len + 1);
        for (int i = 1; i <= len; i++) {
            if (q[i] != i)
                return false;
        }
        return true;
    }
}

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

时间限制: 1.0s 内存限制: 512.0MB 本题总分: 20 分

【问题描述】

爱丽丝刚从一处地下迷宫中探险归来,你能根据她对于自己行动路径的回忆,帮她画出迷宫地图吗?

迷宫地图是基于二维网格的。爱丽丝会告诉你一系列她在迷宫中的移动步骤,每个移动步骤可能是上下左右四个方向中的一种,表示爱丽丝往这个方向

走了一格。你需要根据这些移动步骤给出一个迷宫地图,并满足以下条件:

1、爱丽丝能在迷宫内的某个空地开始,顺利的走完她回忆的所有移动步骤。
2 、迷宫内不存在爱丽丝没有走过的空地。
3 、迷宫是封闭的,即可通过墙分隔迷宫内与迷宫外。任意方向的无穷远处
视为迷宫外,所有不与迷宫外联通的空地都视为是迷宫内。(迷宫地图为四联通,即只有上下左右视为联通)
4 、在满足前面三点的前提下,迷宫的墙的数量要尽可能少。

【输入格式】:

第一行一个正整数N,表示爱丽丝回忆的步骤数量。
接下来一行N个英文字符,仅包含 UDLR 四种字符,分别表示上(Up)、
下(Down)、左(Left)、右(Right)。

【输出格式】

请通过字符画的形式输出迷宫地图。迷宫地图可能包含许多行,用字符‘*’ 表示墙,用‘ ’(空格)表示非墙。
你的输出需要保证以下条件:
1 、至少有一行第一个字符为‘*’
2 、第一行至少有一个字符为‘*’
3 、每一行的最后一个字符为‘*’
4 、最后一行至少有一个字符为‘*’

【样例输入】

17
UUUULLLLDDDDRRRRU

【样例输出】

 *****
*     *
* *** *
* *** *
* *** *
*     *
 *****

【样例说明】

爱丽丝可以把第六行第六个字符作为起点。
外墙墙墙墙墙外
墙内内内内内墙
墙内墙墙墙内墙
墙内墙墙墙内墙
墙内墙墙墙内墙
墙内内内内内墙
外墙墙墙墙墙外

【评测用例规模与约定】

对于所有数据, 0 <N≤ 100

【题解】

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String s = sc.next();
        sc.close();
        int x = 0, y = 0; // 定义起点坐标
        int minX = 0, minY = 0, maxX = 0, maxY = 0; // 定义迷宫的边界
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            switch (c) {
                case 'U': // 上移
                    y--;
                    break;
                case 'D': // 下移
                    y++;
                    break;
                case 'L': // 左移
                    x--;
                    break;
                case 'R': // 右移
                    x++;
                    break;
            }
            // 更新迷宫的边界
            minX = Math.min(minX, x);
            minY = Math.min(minY, y);
            maxX = Math.max(maxX, x);
            maxY = Math.max(maxY, y);
        }
        // 迷宫的宽高
        int width = maxX - minX + 1;
        int height = maxY - minY + 1;
        char[][] maze = new char[height][width]; // 定义迷宫数组
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                maze[i][j] = ' '; // 初始化为非墙
            }
        }
        x = -minX; // 起点在迷宫数组中的坐标
        y = -minY;
        maze[y][x] = '*'; // 起点为墙
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            switch (c) {
                case 'U': // 上移
                    maze[--y][x] = '*'; // 设置为墙
                    break;
                case 'D': // 下移
                    maze[++y][x] = '*'; // 设置为墙
                    break;
                case 'L': // 左移
                    maze[y][--x] = '*'; // 设置为墙
                    break;
                case 'R': // 右移
                    maze[y][++x] = '*'; // 设置为墙
                    break;
            }
        }
        // 输出迷宫
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                System.out.print(maze[i][j]);
            }
            System.out.println();
        }
    }
}

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

时间限制: 1.0s 内存限制: 1.0GB 本题总分: 25 分

【问题描述】

推箱子是一款经典电子游戏,爱丽丝很喜欢玩,但是她有点玩腻了,现在她想设计一款拉箱子游戏。

拉箱子游戏需要玩家在一个N× {\times}×M的网格地图中,控制小人上下左右移动,将箱子拉到终点以获得胜利。

现在爱丽丝想知道,在给定地形(即所有墙的位置)的情况下,有多少种不同的可解的初始局面。

【初始局面】的定义如下:
1 、初始局面由排列成N× {\times}×M矩形网格状的各种元素组成,每个网格中有
且只有一种元素。可能的元素有:空地、墙、小人、箱子、终点。

2 、初始局面中有且只有一个小人。
3 、初始局面中有且只有一个箱子。
4 、初始局面中有且只有一个终点。
【可解】的定义如下:
通过有限次数的移动小人(可以在移动的同时拉箱子),箱子能够到达终点
所在的网格。
【移动】的定义如下:
在一次移动中,小人可以移动到相邻(上、下、左、右四种选项)的一个网格中,前提是满足以下条件:
1 、小人永远不能移动到N× {\times}×M的网格外部。
2 、小人永远不能移动到墙上或是箱子上。
3 、小人可以移动到空地或是终点上。

【拉箱子】的定义如下:

在一次合法移动的同时,如果小人初始所在网格沿小人移动方向的反方向上的相邻网格上恰好是箱子,小人可以拉动箱子一起移动,让箱子移动到小人初始所在网格。

即使满足条件,小人也可以只移动而不拉箱子。

【输入格式】:

第一行两个正整数N和M,表示网格的大小。

接下来N行,每行M个由空格隔开的整数 0 或 1 描述给定的地形。其中
1 表示墙, 0 表示未知的元素,未知元素可能是小人或箱子或空地或终点,但不能是墙。

【输出格式】

输出一个正整数,表示可解的初始局面数量。

【样例输入】

2 4
0 0 0 0
1 1 1 0

【样例输出】

13

【样例说明】

13 种可解的初始局面示意图如下:

人终箱空
墙墙墙空


人终空箱
墙墙墙空


人空终箱
墙墙墙空


箱人终空
墙墙墙空


空人终箱
墙墙墙空


箱终人空
墙墙墙空


空终人箱
墙墙墙空


箱终空人
墙墙墙空


箱空终人
墙墙墙空


空箱终人
墙墙墙空


箱终空空
墙墙墙人


箱空终空
墙墙墙人


空箱终空
墙墙墙人

【评测用例规模与约定】

对于 30 %的数据,N,M≤ 3.
对于 100 %的数据, 0 <N,M≤ 10.

【题解】