2021 第十二届 蓝桥杯 国赛决赛 Java B组 真题 详细解析

2021 第十二届 Java B组 国赛 真题答案 详细解析


结果出来了,国三,考前准备的时间还是太少了 就考前四五天刷了一些往年的题,前面的时间在写别的项目
该死,最后那道题我应该暴搜混分的,可惜当时样例输出一下就走了
哎,蓝桥杯不能看到每道题的答题情况,我都不知道我那两道超了内存限制的题最后是怎么样了,是直接没分了还是?
本人大一,明年再好好准备再参加一次吧




试题A: 整数范围

本题总分:5 分
【问题描述】

  用8位二进制(一个字节)来表示一个非负整数,表示的最小值是0,则一般能表示的最大值是多少?

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

答案:255
分析:

  签到题,二进制00000000为十进制的0,11111111自然为十进制的255。

Code:

  无


试题B: 纯质数

本题总分:5 分
【问题描述】

  如果一个正整数只有1和它本身两个约数,则称为一个质数(又称素数)。
  前几个质数是:2,3,5,7,11,13,17,19,23,29,31,37…。
  如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:2,3,5,7,23,37都是纯质数,而11,13,17,19,29,31不是纯质数。当然1,4,35也不是纯质数。
  请问,在1到20210605中,有多少个纯质数?

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

答案:1903
分析:

  先找出1~20210605中所有的质数(可以先打表),然后再遍历所有找出的质数,判断是不是纯质数。至于如何判断某个质数n是不是纯质数,这里如果自己手动去把该质数n的每一位取出来会比较麻烦,所以用java自带的类来快速实现:将该质数n用String.value(int value)转换为字符串,然后用for遍历这个字符串中每一个字符,再用Integer.value(String value)将该字符转化为数字,再单独判断每个这个数字是不是质数就行,记得1,和0不是质数要排除掉。
(虽然这样暴搜运行效率底,但是实现快,填空题嘛。把上限调到较小的值测试测试耗时之后,觉得还行,无非就几分钟,这几分钟我先去看别的题目,回来之后就有结果了,实在不行,骚微优化一下也行,不过现场还没优化就出来了,码字不易,要一个赞不过分吧)

Code:

比赛时现场敲的原代码,后面有时间再写优化版的
这里先打上注释

import java.util.ArrayList;
public class Main {
	static int number=20210605;//测试的上限
	static int map[]=new int[number];
	static ArrayList<String> ans=new ArrayList(); 
	static int anscount=0;
	
	public static void main(String[] args) {
		int count=0;
		//此for循环找出范围内所有的质数
		for (int i = 2; i <= number; i++) {
			boolean add=true;
			for (int j = 2; j*j <= i; j++) {
				if(i%j==0) {
					add=false;
					continue;
				}
			}
			if(add==true) {
				map[count]=i;
				count++;
			}
		}
//		System.out.println("wanbi");
		//此for循环对每个质数进行判断
		for (int i = 0; i < count; i++) {
			String single = String.valueOf(map[i]);
//			System.out.print(single+"   ");
			boolean shi=true;
			//将这个质数转化为字符串后将其的每一个字符转化为数字
			flag: for (int j = 0; j < single.length(); j++) {
				Integer wei = Integer.valueOf(single.charAt(j)-48);
				//排除1和0
				if(wei==1 || wei==0) {
					shi=false;
					break;
				}
				//判断这个位数是不是质数
				for (int k = 2; k*k <=wei; k++) {
					if(wei%k==0) {
						shi=false;
						break flag;
					}
				}
//				System.out.print(wei+"  ");
			}
			//如果这个质数是纯质数的话,总和+1
			if(shi==true) {
				System.out.println(single);
				ans.add(single);
				anscount++;
			}
//			System.out.println();
			
		}
		//调试时的输出语句
//		for (int i = 0; i < count; i++) {
//			System.out.print(map[i]+" ");
//		}
//		System.out.println();
//		for (int i = 0; i < ans.size(); i++) {
//			System.out.print(ans.get(i)+" ");
//		}
		System.out.println();
		System.out.println(anscount);
	}
}

  


试题C: 完全日期

本题总分:10 分
【问题描述】

  如果一个日期中年月日的各位数字之和是完全平方数,则称为一个完全日期。
  例如:2021年6月5日的各位数字之和为2 + 0 + 2 + 1 + 6 + 5 = 16,而16是一个完全平方数,它是4的平方。所以2021年6月5日是一个完全日期。
  例如:2021年6月23日的各位数字之和为2 + 0 + 2 + 1 + 6 + 2 + 3 = 16,是一个完全平方数。所以2021年6月23日也是一个完全日期。
  请问,从2001年1月1日到2021年12月31日中,一共有多少个完全日期?

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

答案:977
分析:

  这个题和上个题一样,直接上java自带的日期类Data和Calendar,总比现场自己实现来的快。所以如果蓝桥杯报的是java的话,一定要多了解这些日期,时间,集合等这些自带类的基本使用。就像你报C/C++一定要会用STL一样。说不定就用上了,用上了直接就爽歪了(手动滑稽),而且比赛有提供api手册的,不用白不用。要知道2001.01.01~2021.12.31有多少个完全日期的话需要把所有日期都遍历一遍,如果你自己实现的话,那你必须把每一天都遍历到,不能多了不能少了,即你需要对大小月进行判断,比如2月28天的,也有29天的,其他月份有30天的,也有31天的。当你把这些判断清楚后已经花费很多时间了。所有还是用java自带的类来得快。(其实最主要的是我判断润不闰年的那个条件记不太清楚了,是什么什么400的整倍?还是怎么招,不太确定,所以当时就想着用java自带的类库稳妥一点)
  首先是获取到日期指定为2001.01.01的Data类的对象data,然后再将此对象data转换为Calendar类的对象calendar,为什么要转换为Calendar类的对象呢,以为Data类没有提过具体的日期的增加,减少的方法(即在原日期上增加一个年,月,天,小时,秒等)。Calendar类有提供这些方法,利用其提供的:在原日期上增加一天的方法 ,我们就可以很快的实现对2001.01.01~2021.12.31中的每一天进行遍历,而且什么大小月,闰年非闰年,java官方已经帮我们解决好了,你说它不香吗。在遍历的时候我们可以调用对象calendar的gei(int filed)方法,快速拿到每一天的号数,月数,年份。但是这里有个坑,就是通过此方法拿到的数值是和对象calendar调用toString后看到的字符串上的日期是有差距的(比如每个月1号本来获取到的数值应该是1,而实际情况却是0等不一致的情况)。而且转换还不是很方便。试了两边后现场的我果断从calendar的toString中获取数值(即在calendar调用toString后得到的字符串中截取数值),这样就避免了刚刚提到了那个不一致的情况。
  拿到数值后就很快了,累加一下判断一下是不是完全平方数就行了。这一步我也是利用了java自带的类来快速实现。(即把几个可能的完全平方数加到ArrayList数组里面,然后调用contains方法就可以知道某个数是不是完全平方数了)

Code:

比赛时现场敲的原代码,后面有时间再写优化版的
这里先打上注释



import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class Main {

    static int anscount=0;

    public static void main(String[] args) throws ParseException {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");//指定输入格式
        Date data = simpleDateFormat.parse("2001/01/01");//获取指定日期的Data对象
//		System.out.println(data);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(data);//转化为对应的Calendar类的对象,以便利用其内现有的方法
        calendar.add(Calendar.DAY_OF_YEAR, 1);//获取后相差了一天,把它加回去
//		System.out.println(calendar.toInstant());


        int[] list= new int[10];
        ArrayList<Integer> index=new ArrayList();

        for (int i = 1; i <= 20; i++) {
            index.add(i*i);
        }
		//输出一下,看产生的完全平方数对不对
//        for (int i = 0; i <index.size(); i++) {
//            System.out.print(index.get(i)+"  ");
//        }
//        System.out.println();

        for (int i = 1; i <= 7670; i++) {
            StringBuilder yearsb=new StringBuilder();
            StringBuilder monthsb=new StringBuilder();
            StringBuilder daysb=new StringBuilder();
			//太多数据看不过来,调试时将下面这个if打开,筛掉一部分数据
//			if(i<100 ||i>7600) {
            String datastr = calendar.toInstant().toString();//将此时calendar储存的日期转化为字符串
            //下面三个for从该字符串中获取年份的字符,
            for (int j = 0; j < 4; j++) {
                yearsb.append(datastr.charAt(j));
            }
            for (int j = 5; j < 7; j++) {
                monthsb.append(datastr.charAt(j));
            }
            for (int j = 8; j < 10; j++) {
                daysb.append(datastr.charAt(j));
            }
			//紧接这的三个for将字符转换为数字
            int count=0;
            for (int j = 0; j < yearsb.length(); j++) {
                list[count]=Integer.valueOf(yearsb.charAt(j)-48);
                count++;
            }
            for (int j = 0; j < monthsb.length(); j++) {
                list[count]=Integer.valueOf(monthsb.charAt(j)-48);
                count++;
            }
            for (int j = 0; j <daysb.length(); j++) {
                list[count]=Integer.valueOf(daysb.charAt(j)-48);
                count++;
            }
            //对所有数字进行累加
            int sum=0;
            for (int j = 0; j < count; j++) {
//					System.out.print(list[j]+" ");
                sum+=list[j];
            }
            //判断是否在已经建立好的完全平方数表里面
            boolean contains = index.contains(sum);
            //是的话计数器+1
            if(contains==true) {
//                System.out.println("  "+sum);
                anscount++;
            }
//			}
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        System.out.println(anscount);
    }
}




推荐几本书

这几本都是入门算法非常经典的书,可以看看,也可以当工具书

在这里插入图片描述
下载链接

https://gitee.com/Tptogiar/cs-book





试题D: 最小权值

本题总分:10 分
【问题描述】

  对于一棵有根二叉树T,小蓝定义这棵树中结点的权值W(T)如下:
  空子树的权值为0。
  如果一个结点v有左子树L,右子树R,分别有C(L)和C®个结点,则:
    W(v) = 1 + 2W(L) + 3W® +(C(L))2C®。
  树的权值定义为树的根结点的权值。
  小蓝想知道,对于一棵有2021个结点的二叉树,树的权值最小可能是多少?

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

答案:
分析:现场的时候不会做

  考前在写一个局域网的聊天工具,后面又写了一个安卓仿微信的界面,快考时一整晚一整晚的打cs:go

所以数据结构什么的,树什么的只是了解过,但是没有实战过。所以看见这道题直接跳了
动态规划的题也刷的少,大概写过的动归只有3,4道吧。所以考前一晚上慌得一匹,找了一些动归的资料,学了一些。比赛的时候真久用上了,不过用的不太对(下面试题G: 和与乘积 )

Code:

  


试题E: 大写

时间限制: 1.0s  内存限制: 512.0MB  本题总分:15分
【问题描述】

  给定一个只包含大写字母和小写字母的字符串,请将其中所有的小写字母转换成大写字母后将字符串输出。

【输入格式】
输入一行包含一个字符串
【输出格式】
输出转换成大写后的字符串。
【样例输入1】
LanQiao
【样例输出1】
LANQIAO
【评测用例规模与约定】
对于所有评测用例,字符串的长度不超过100。

分析:

  签到题,用什么都快,但是用java中String自带的大小写转换函数更快,不到30s,新的java文件刚建完就写完了。不过也是简单,我越容易犯错误,题目重新看了几遍,代码测试了几遍我才敢交。

Code:

比赛时现场敲的原代码

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		String next = cin.next();
		String upperCase = next.toUpperCase();
		System.out.println(upperCase);
	}
}

试题F: 123

时间限制: 5.0s   内存限制: 512.0MB   本题总分:15分
【问题描述】

  小蓝发现了一个有趣的数列,这个数列的前几项如下:
    1, 1, 2, 1, 2, 3, 1, 2, 3, 4, …
  小蓝发现,这个数列前1项是整数1,接下来2项是整数1至2,接下来3项是整数1至3,接下来4项是整数1至4,依次类推。
  小蓝想知道,这个数列中,连续一段的和是多少。

【输入格式】
输入的第一行包含一个整数T,表示询问的个数。
接下来T行,每行包含一组询问,其中第i行包含两个整数li和ri,表示询问数列中第li个数到第ri个数的和。
【输出格式】
输出T行,每行包含一个整数表示对应询问的答案。
【样例输入】
3
1 1
1 3
5 8
【样例输出】
1
4
8
【评测用例规模与约定】
在这里插入图片描述

分析:

  用规律去算了,可以过前面的数据

Code:

比赛时现场敲的原代码,后面有时间再写优化版的
这里先打上注释


试题G: 和与乘积

时间限制: 1.0s   内存限制: 512.0MB   本题总分:20分
【问题描述】

给定一个数列A= ( a 1 {a_{1}} a1, a 2 {a_{2}} a2,…, a n {a_{n}} an)
问有多少个区间[L,R]满足区间内元素的乘积等于他们的和,即 a L {a_{L}} aL * a L + 1 {a_{L+1}} aL+1 * …* a R {a_{R}} aR= a L {a_{L}} aL+ a L + 1 {a_{L+1}} aL+1+…+ a R {a_{R}} aR

【输入格式】
输入第一行包含一个整数n,表示数列的长度。第二行包含n个整数,依次表示数列中的数 a 1 {a_{1}} a1, a 2 {a_{2}} a2,…, a n {a_{n}} an
【输出格式】
输出仅一行,包含一个整数表示满足如上条件的区间的个数。
【样例输入】
4
1 3 2 2
【样例输出】
6
【样例解释】
符合条件的区间为[1,1],[1,3],[2,2],[3,3],[3,4],[4,4]。
【评测用例规模与约定】
在这里插入图片描述

分析:
 比赛时的思路

  题目要求找出所有满足要求的子区间,可以找出所有区间,然后看着每个区间是否满足题意。

那如何找出所有区间呢?如果用传统的递归遍历或是循环遍历,那无疑复杂度会很高。所以我想到的就是用动态规划。

例如:
对于一个{1,3,2,2}这个集合,它的所有子区间无非就是[1,1],[1,2],[1,3],[1,4],[2,2],[2,3],[2,4],[3,3],[3,4],[4,4]。如果把它画在一个4*4的矩阵中那就是取矩阵的上上三角(即主对角线往上那部分,下面示意图中有具体值填充的非空的区域)。

那如何进行累加累乘的计算?如果每一个区间的累加值和累乘值都从头计算一遍那样复杂度也会很高。所以需要运用递推的思想(这种思想还是挺有用的,类似于分治思想,把问题分解为多个子问题再逐一计算,且计算的数据源来自于上一次计算的结果)。运用递推将上一次结算的结果作为本次计算的数据源,这样复杂度就降下来了。

具体操作上:
开一个dp数组,即dp[count][count][2]。其中dp的第三维为[2],其第一个[0]用来存放累加的值,第二个[1]用来存放累乘的积。即dp[i][j][0]表示从第i行的第i个累计加到该行第j个的值,dp[i][j][1]表示从第i行的第i个累乘到该行第j个的值

状态转移方程为:

(1)dp[i][j][0]=dp[i][j-1][0]+list[j]
(表示第i行的第j个的累加值等于该行上一列的累加值加上该列本身的值)

(2)dp[i][j][1]=dp[i][j-1][1] * list[j]
(表示第i行的第j列的累乘值等于该行上一列的累乘值乘上该列本身的值)

最后在根据计算过的结果遍历一遍看有那些是符合题意的就可以了
下面是示意图(红色的为累加的值等于累乘的值,即题目所求)
在这里插入图片描述
做图不易,正在看图的彭于晏CSDN分晏会留下一个赞的吧。
在这里插入图片描述

考前刷的题少,刚好考前一晚看了一些动规中最长公共子序列相关的资料。了解到了那种思想。所以看到这道题,我第一反应就是动规动规动规~动动规,可惜这道题动归不是正解😣。用动归过不了全部的数据(数据量级数组存不下)。虽然说我的这种方法不是正解,但我觉得这种方法还是值得详细讲一下的,在一些别的题目中这种思想还是挺常见的。
20210607:糟了今天重新来看这道题发现不太对劲,题目限制内存512MB, 我开的数组肯定超内存了。


 下面是正解分析👇

有时间再给大家奉上正解吧。

Code:

比赛时写的代码(动归),后面有时间再做一遍再贴出来

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Main {
    static int dp[][][]=new int[10002][10002][2];
    static int list[]=new int[20002];
    static int n;

    public static void main(String[] args) throws IOException {

        Scanner cin = new Scanner(System.in);
        n=cin.nextInt();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in),1024);
//		System.out.println("test1");
        String[] split = br.readLine().split(" ");//读入一整行,在用空格把这些数字分开

        for (int i = 1; i <= split.length; i++) {
            list[i]=Integer.valueOf(split[i-1]);//将字符串类型的数字转化为int类型,并存进数组里面
        }
//		System.out.println("test2");
		//下面两个for用于初始化dp数组(初始化上文示意图中4个黑色箭头所指的地方)
        for (int i = 1; i <= n; i++) {
            for (int j = i; j <=n; j++) {
                if(i==j) {
                    dp[i][j][0]=list[i];
                    dp[i][j][1]=list[i];
                }
            }
        }

		//
        for (int i = 1; i <= n; i++) {
            for (int j = i+1; j <=n; j++) {
                dp[i][j][0]=dp[i][j-1][0]+list[j];
                dp[i][j][1]=dp[i][j-1][1]*list[j];
            }
        }

        int ans=n;
        for (int i = 1; i <= n; i++) {
            for (int j = i+1; j <=n; j++) {
                if(dp[i][j][0]==dp[i][j][1]) {
                    ans++;
                }
            }
        }


//		for (int i = 1; i <= n; i++) {
//			for (int j = 1; j <=n; j++) {
//				System.out.print("("+dp[i][j][0]+" "+dp[i][j][1]+")");
//			}
//			System.out.println();
//		}

        System.out.println(ans);
    }
}

试题H: 巧克力

时间限制: 1.0s    内存限制: 512.0MB    本题总分:20分
【问题描述】

  小蓝很喜欢吃巧克力,他每天都要吃一块巧克力。
  一天小蓝到超市想买一些巧克力。超市的货架上有很多种巧克力,每种巧克力有自己的价格、数量和剩余的保质期天数,小蓝只吃没过保质期的巧克力,
  请问小蓝最少花多少钱能买到让自己吃x天的巧克力。

【输入格式】
输入的第一行包含两个整数x,n,分别表示需要吃巧克力的天数和巧克力的种类数。接下来n行描述货架上的巧克力,其中第i行包含三个整数 a i {a_{i}} ai, b i {b_{i}} bi, c i {c_{i}} ci,表示第i种巧克力的单价为 a i {a_{i}} ai,保质期还剩 b i {b_{i}} bi天(从现在开始的 b i {b_{i}} bi天可以吃),数量为 c i {c_{i}} ci
【输出格式】
输出一个整数表示小蓝的最小花费。
如果不存在让小蓝吃x天的购买方案,输出-1。
【样例输入】
10 3
1 6 5
2 7 3
3 10 10
【样例输出】
18
【样例说明】
一种最佳的方案是第1种买5块,第2种买2块,第3种买3块。
前5天吃第1种,第6、7天吃第2种,第8至10天吃第3种。
【评测用例规模与约定】
在这里插入图片描述

分析:

  做了,提交上去了。但是感觉不对,有些数据会出错,应该能拿一些分。

Code:

  


试题I: 翻转括号序列

时间限制: 10.0s    内存限制: 768.0MB    本题总分:25分
【问题描述】

给定一个长度为n的括号序列,要求支持两种操作:
  1.将[ L i {L_{i}} Li R i {R_{i}} Ri]区间内(序列中的第 L i {L_{i}} Li个字符到第 R i {R_{i}} Ri个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
  2.求出以 L i {L_{i}} Li为左端点时,最长的合法括号序列对应的 R i {R_{i}} Ri(即找出最大的Ri使[ L i {L_{i}} Li, R i {R_{i}} Ri]是一个合法括号序列)。

【输入格式】
输入的第一行包含两个整数n,m,分别表示括号序列长度和操作次数。第二行包含给定的括号序列,括号序列中只包含左括号和右括号。接下来m行,每行描述一个操作。如果该行为“1 L i {L_{i}} Li R i {R_{i}} Ri”,表示第一种操作,区间为[ L i {L_{i}} Li, R i {R_{i}} Ri];如果该行为“2 L i {L_{i}} Li”表示第二种操作,左端点为 L i {L_{i}} Li
【输出格式】
对于每个第二种操作,输出一行,表示对应的 R i {R_{i}} Ri。如果不存在这样的 R i {R_{i}} Ri,请输出0。
【样例输入】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【样例输出】
4
7
0
0
【评测用例规模与约定】
在这里插入图片描述

分析:

  
考前这种括号比配的题没刷过,直接使用样例输出大法了😂

Code:

  


试题J: 异或三角

时间限制: 5.0s    内存限制: 512.0MB    本题总分:25分
【问题描述】

给定T个数 n 1 {n_{1}} n1, n 2 {n_{2}} n2,…, n T {n_{T}} nT,对每个 n i {n_{i}} ni请求出有多少组a,b,c满足:
在这里插入图片描述
【输入格式】
输入的第一行包含一个整数T。
接下来T行每行一个整数,分别表示 n 1 {n_{1}} n1, n 2 {n_{2}} n2,…, n T {n_{T}} nT
【输出格式】
输出T行,每行包含一个整数,表示对应的答案。
【样例输入】
2
6
114514
【样例输出】
6
11223848130
在这里插入图片描述
在这里插入图片描述

分析:

  
考前这种题也没刷过,直接样例输出大法😂

Code:

  

评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值