第十一届蓝桥杯 第二场 Java B组 2020-10-17

试题 A: 门牌制作

【问题描述】 小蓝要为一条街的住户制作门牌号。 这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字 符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、 0、1、 7,即需要 1 个 字符 0, 2 个字符 1, 1 个字符 7。 请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符2?
【答案提交】 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:填空题,填充答案即可,遍历1到2020所有数字,将每个数字包含的2的个数相加即可得出答案:624

public class Main {
    
    
	public static void main(String[] args) {
    
    
		int count_2 = 0;
		for (int i = 1; i <= 2020; i++) {
    
    
			if (i / 1000 == 2) {
    
    
				count_2++; //判断千位
			}
			if (i % 1000 / 100 == 2) {
    
    
				count_2++;//判断百位
			}
			if (i % 100 / 10 == 2) {
    
    
				count_2++;//判断十位
			}
			if (i % 10 == 2) {
    
    
				count_2++;//判断个位
			}
		}
		System.out.println(count_2);
	}
}

试题 B: 寻找 2020

【问题描述】 小蓝有一个数字矩阵,里面只包含数字 0 和 2。小蓝很喜欢 2020,他想找 到这个数字矩阵中有多少个 2020 。小蓝只关注三种构成 2020 的方式: • 同一行里面连续四个字符从左到右构成 2020。 • 同一列里面连续四个字符从上到下构成2020。 • 在一条从左上到右下的斜线上连续四个字符,从左上到右下构成 2020。 例如,对于下面的矩阵: 220000 000000002202 000000 000022 002020 一共有 5 个 2020。其中 1 个是在同一行里的, 1 个是在同一列里的, 3个 是斜线上的。 小蓝的矩阵比上面的矩阵要大,由于太大了,他只好将这个矩阵放在了一 个文件里面,在试题目录下有一个文件2020.txt,里面给出了小蓝的矩阵。 请帮助小蓝确定在他的矩阵中有多少个 2020。

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

不会。。。。。。

试题 C: 蛇形填数

【问题描述】 如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。 如下列矩阵。容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20 行第 20 列 的数是多少?
【答案提交】这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
=1 2 6 7
=3 5 8
=4 9
=10

一开始想一个数一个数列出来,写到一半的时候发现想要得到第20行20列的数字,得来回摆动40几次-.-。然后回头写代码(:

public class Main {
    
    
	public static void main(String[] args) {
    
    
		int[][] nums = new int[100][100];

		nums[0][0] = 1;// 初始化
		for (int m = 1; m <= 50; m++) {
    
    
			if (m % 2 == 1) {
    
    
				nums[0][m] = nums[0][m - 1] + 1;
				int i = 1;
				int j = m - 1;
				while (j >= 0) {
    
    
					nums[i][j] = nums[i - 1][j + 1] + 1;
					i++;
					j--;
				}
			} else {
    
    
				nums[m][0] = nums[m - 1][0] + 1;
				int i = m - 1;
				int j = 1;
				while (i >= 0) {
    
    
					nums[i][j] = nums[i + 1][j - 1] + 1;
					i--;
					j++;
				}
			}
		}
		for (int i = 0; i < nums.length; i++) {
    
    
			for (int j = 0; j < nums[0].length; j++) {
    
    
				System.out.print(nums[i][j] + " ");
			}
			System.out.println();
		}
	}
}

输出结果如下:没有数错的话解为761 (啊!好像我对这个数字没印象,当时这不会做错吧)
在这里插入图片描述

试题 D: 七段码

【问题描述】 小蓝要用七段码数码管来表示一种特殊的文字。
在这里插入图片描述
上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a, b, c, d, e, f, g。
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符 的表达时,要求所有发光的二极管是连成一片的。 例如: b
发光,其他二极管不发光可以用来表达一种字符。 例如: c 发光,其他二极管不发光可以用来表达一种字符。这种 方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。 例如: a, b, c, d, e 发光, f, g 不发光可以用来表达一种字符。
例如: b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光 的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

这道题纯数出来的,不过该题中1–6,2–5,3–4 分别对称。
解是80…

试题 E: 排序 本题总分:

【问题描述】 小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。 在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串lan排序,只需要1次交换。对于字符串qiao
排序,总共需要4次交换。小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要100次交换,可是他忘了把这个字符串记下来,现在找不到了。
请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要100次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中不可以包含相同的字符。
【答案提交】 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个只包含小写英文字母的字符串,在提交答案时只填写这个字符串,填写多余的内容将无法得分。

根据题目信息,qiao排序需要4次交换,lan排序需要1次交换,则排序为升序。所以如果想要字符串最短,字母无重复并满足100次交换,则字典序最大的应该放在字符串头部(贪心),且从左向右,每个字母需要交换的次数呈递减排列(等差为1),则推导出满足条件的字符串为:jonmlkjihgfedcba

可能不对,但这是我目前能想到的一个最符合题意的答案(相比之下字典序最小)。

public class Main {
    
    
	public static void main(String[] args) {
    
    
		char[] s = "jonmlkihgfedcba".toCharArray();
		int count = 0;
		for (int j = 0; j < s.length - 1; j++) {
    
    
			for (int i = 0; i < s.length - j - 1; i++) {
    
    
				if (s[i] > s[i + 1]) {
    
    
					char temp = s[i];
					s[i] = s[i + 1];
					s[i + 1] = temp;
					count++;
				}
			}
		}
		for (char c : s) {
    
    
			System.out.print(c);
		}
		System.out.println();
		System.out.println(count); 

	}
}

试题 F: 成绩分析

时间限制: 1.0s 内存限制: 512.0MB

【问题描述】 小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是 一个 0 到 100 的整数。请计算这次考试的最高分、最低分和平均分。

【输入格式】 输入的第一行包含一个整数 n,表示考试人数。 接下来 n 行,每行包含一个 0 至 100 的整数,表示一个学生的得分。

【输出格式】 输出三行。 第一行包含一个整数,表示最高分。 第二行包含一个整数,表示最低分。 第三行包含一个实数,四舍五入保留正好两位小数,表示平均分。

【样例输入】 7 80 92 56 74 88 99 10
【样例输出】 99 10 71.29
【评测用例规模与约定】
对于 50% 的评测用例, 1 ≤ n ≤ 100。
对于所有评测用例, 1 ≤ n ≤ 10000。

import java.util.Scanner;

public class Main {
    
    
	public static void main(String[] args) {
    
    
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int sum = 0;
		int max = -1;
		int min = 101;
		//题目中明确说明学生人数n>=1,这样定义max,min是为了100分,0分这样的极端情况
		for (int i = 0; i < n; i++) {
    
    
			int grade = in.nextInt();
			max = grade > max ? grade : max;
			min = grade < min ? grade : min;
			sum += grade;
		}
		System.out.println(max);
		System.out.println(min);
		System.out.println(String.format("%.2f", (double) sum / n));
	}
}

试题 G: 单词分析

时间限制: 1.0s 内存限制: 512.0MB

【问题描述】
小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出现得最多来分辨单词。现在,请你帮助小蓝,给了一个单词后,帮助他找到出现最多的字母和这个字母出现的次数

【输出格式】 输出两行,第一行包含一个英文字母,表示单词中出现得最多的字母是哪个。如果有多个字母出现的次数相等,输出字典序最小的那个。第二行包含一个整数,表示出现得最多的那个字母在单词中出现的次数。

【样例输入】 lanqiao 【样例输出】 a 2

【样例输入】 longlonglongistoolong 【样例输出】 o 6
【评测用例规模与约定】
对于所有的评测用例,输入的单词长度不超过 1000。

使用int[26]来记录每个字母的次数,遇到大值就更新:

import java.util.Scanner;

public class Main {
    
    
	public static void main(String[] args) {
    
    
		Scanner in = new Scanner(System.in);
		String S = in.next();
		int[] arr = new int[26]; // 用来记录每个字母出现的次数
		char[] s = S.toCharArray();// 将字符串转化成数组,便于后续操作
		for (int i = 0; i < s.length; i++) {
    
    
			arr[s[i] - 'a']++;
		}
		int max = arr[0];
		char ans = 'a';
		for (int i = 0; i < s.length; i++) {
    
    
			if (arr[i] > max) {
    
    
				//只要后面的元素大于max才进行更新(保证max所对应的字母字典序最小)
				ans = (char) (i + 'a');
				max = arr[i];
			}
		}
		System.out.println(ans);
		System.out.println(max);
	}
}

试题 H: 数字三角形

时间限制: 1.0s 内存限制: 512.0MB
【问题描述】
在这里插入图片描述
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。

【输入格式】 输入的第一行包含一个整数 N (1 < N ≤ 100),表示三角形的行数。下面的 N 行给出数字三角形。数字三角形上的数都是
0 至 100 之间的整数。
【输出格式】 输出一个整数,表示答案。

【样例输入】 57 3 8 8 1 0 2 7 4 4 4 5 2 6 5
【样例输出】 27

这道题第一遍做的时候没有注意“向左下走的次数与向右下走的次数相差不能超过 1。”直接dp数组薅下来,幸亏与样例答案不符合。才重新读了一遍题。(一定要仔细读题)。发现问题所在,但动态规划的方法想了很久未果,最后使用暴力…(出奇迹啊拜托)

import java.util.Scanner;

public class Main {
    
    
	public static void main(String[] args) {
    
    
		Scanner in = new Scanner(System.in);
		int N = in.nextInt();
		int[][] nums = new int[N][N];
		for (int i = 0; i < N; i++) {
    
    
			for (int j = 0; j <= i; j++) {
    
    
				nums[i][j] = in.nextInt();
			}
		}
		System.out.println(dfs(nums, 0, 0, 0, 0));
	}

	public static int dfs(int[][] nums, int i, int j, int left, int right) {
    
    
		if (i == nums.length - 1) {
    
    
			//如果向左下走的次数与向右下走的次数相差超过 1
			if (Math.abs(left - right) > 1) {
    
    
				return 0;
			}
		}
		
		if (i < nums.length) {
    
    
		//要么向左走,要么向右走
			return nums[i][j]
					+ Math.max(dfs(nums, i + 1, j, left + 1, right), dfs(nums, i + 1, j + 1, left, right + 1));
		}
		return 0;
	}
}

试题 I: 子串分值和

时间限制: 1.0s 内存限制: 512.0MB

【问题描述】 对于一个字符串 S,我们定义 S 的分值 f (S ) 为 S 中出现的不同的字符个 数。例如 f (”aba”) = 2,
f (”abc”) = 3, f (”aaa”) = 1。 现在给定一个字符串 S [0::n − 1](长度为 n),请你计算对于所有 S的非空 子串 S [i:: j](0 ≤ i ≤ j < n), f (S [i:: j]) 的和是多少。

【输入格式】 输入一行包含一个由小写字母组成的字符串 S。
【输出格式】 输出一个整数表示答案。
【样例输入】 ababc
【样例输出】28
【样例说明】子串 f 值
a 1
ab 2
aba 2
abab 2
ababc 3
b 1
ba 2
bab 2
babc 3
a 1
ab 2
abc 3
b 1
bc 2
c 1
【评测用例规模与约定】 对于 20% 的评测用例, 1 ≤ n ≤ 10; 对于 40% 的评测用例, 1 ≤ n ≤ 100; 对于 50% 的评测用例, 1 ≤ n ≤ 1000; 对于 60% 的评测用例, 1 ≤ n ≤ 10000;
对于所有评测用例, 1 ≤ n ≤ 100000。

典型的动态规划例题、
我们先研究每个字符串本身,即研究该字符串的始终包含第一个元素的子串(因为“abc”的所有非空子串等价于“a”,“ab”,“abc”的所有包含第一个元素的子串)。通过研究样例发现(从后往前推导),对于新增到字符串S头部的字母x,有以下几种情况:
在这里插入图片描述
当原字符串S中不包含x,则新增的字符个数为 S.length+1
当原字符串S中包含x,新增的字符个数取决于该字符首次出现的位置。即S.indexOf(x) + 1

创建dp数组,dp[i]记录从0到 i 长度的字符串,包含首字母的所有子串的字符个数。dp[0] = 1

代码如下:

import java.util.Scanner;

public class Main {
    
    
	public static void main(String[] args) {
    
    
		Scanner in = new Scanner(System.in);
		String S = in.next();
		char[] s = S.toCharArray();
		StringBuilder sb = new StringBuilder(S);
		int[] dp = new int[s.length];
		dp[s.length - 1] = 1;
		//依照案例,从后往前判断
		for (int i = s.length - 2; i >= 0; i--) {
    
    
			// 对于新增的元素,判断上一子串中是否含有它
			int index = sb.substring(i + 1, s.length).indexOf(s[i]);
			// 如果没有,新增字符数为 上一子串长度+1
			// 否则,新增字符数为该字符在原字符串中首次出现的位置
			dp[i] = dp[i + 1] + (index == -1 ? s.length - i : index + 1);
		}
		int sum = 0;
		for (int i : dp) {
    
    
			sum += i;
		}
		System.out.println(sum);
	}
}

代码正不正确还不清楚,我想说的是,为什么不仔细一点,这么大的数据,int不越界就怪了。(啊!) 刚考完刚反应过来,唉,还是这种考试参加的少,以为做出来就万事大吉了。

(我只是一个接触算法不太久的小白,这是我前几天在考场上写出的答案,并不一定是正确答案。欢迎各位大佬批评改正,留下宝贵的见解)

10月26日:今天成绩出来啦,省一稳稳拿下(尽管有好多不该错的题),但是好像不能去北京(¬㉨¬)

最后一题,在考场上的时候看都没看…

猜你喜欢

转载自blog.csdn.net/qq_45908922/article/details/109144138