第十二届蓝桥杯省赛第二场 Java B组真题个人题解

结果填空题

试题A: 求余

本题总分:5 分
【问题描述】
在 C/C++/Java/Python 等语言中,使用 % 表示求余,请问 2021%20 的
值是多少?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】

1

白送的热身题。

试题 B: 双阶乘

本题总分:5 分
【问题描述】
一个正整数的双阶乘,表示不超过这个正整数且与它有相同奇偶性的所有
正整数乘积。n 的双阶乘用 n!! 表示。
例如:
3!! = 3 × 1 = 3。
8!! = 8 × 6 × 4 × 2 = 384。
11!! = 11 × 9 × 7 × 5 × 3 × 1 = 10395。
请问,2021!! 的最后 5 位(这里指十进制位)是多少?
注意:2021!! = 2021 × 2019 × · · · × 5 × 3 × 1。
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】

59375

【思路】

简单的枚举即可,需要注意的是只要最后五位,所以要模100000,而且必须是每次累乘之后都要模,因为数字太大肯定会溢出的,到最后才取模的话,数字就已经是溢出之后的了,那么结果肯定有问题的。

【代码】

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		long ans = 1;
		for(int i = 3; i <= 2021; i+=2)
		{
			ans *= i;
			ans %= 100000;
		}
		System.out.println(ans);
		
		in.close();
	}

}

试题 C: 格点

本题总分:10 分
【问题描述】
如果一个点 (x, y) 的两维坐标都是整数,即 x ∈ Z 且 y ∈ Z,则称这个点为
一个格点。
如果一个点 (x, y) 的两维坐标都是正数,即 x > 0 且 y > 0,则称这个点在
第一象限。
请问在第一象限的格点中,有多少个点 (x, y) 的两维坐标乘积不超过 2021, 即 x · y ≤ 2021。
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】

15698

【思路】

枚举x和y,范围都是1到2021,判断x*y<=2021计数即可。

【代码】

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int ans = 0;
		for(int x = 1; x <= 2021; x++)
		{
			for(int y = 1; y <= 2021; y++)
			{
				if(x * y <= 2021)
				{
					ans++;
				}
			}
		}
		System.out.println(ans);
		
		in.close();
	}

}

试题 D: 整数分解

本题总分:10 分
【问题描述】
将 3 分解成两个正整数的和,有两种分解方法,分别是 3 = 1 + 2 和
3 = 2 + 1。注意顺序不同算不同的方法。
将 5 分解成三个正整数的和,有 6 种分解方法,它们是 1+1+3 = 1+2+2 =
1 + 3 + 1 = 2 + 1 + 2 = 2 + 2 + 1 = 3 + 1 + 1。
请问,将 2021 分解成五个正整数的和,有多少种分解方法?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】

691677274345

【思路】

暴力破解的话,2021的五次方几个小时都运行不出结果,一种可行的办法是使用数学中排列组合公式算出结果(哎,,,这道题赛场上就没做出来)

2020×2019×2018×2017÷4÷3÷2

【代码】

//数学方法,排列组合

试题 E: 城邦

本题总分:15 分
【问题描述】
小蓝国是一个水上王国,有 2021 个城邦,依次编号 1 到 2021。在任意两
个城邦之间,都有一座桥直接连接。
为了庆祝小蓝国的传统节日,小蓝国政府准备将一部分桥装饰起来。
对于编号为 a 和 b 的两个城邦,它们之间的桥如果要装饰起来,需要的费
用如下计算:找到 a 和 b 在十进制下所有不同的数位,将数位上的数字求和。
例如,编号为 2021 和 922 两个城邦之间,千位、百位和个位都不同,将这
些数位上的数字加起来是 (2 + 0 + 1) + (0 + 9 + 2) = 14。注意 922 没有千位,千
位看成 0。
为了节约开支,小蓝国政府准备只装饰 2020 座桥,并且要保证从任意一个
城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。
请问,小蓝国政府至少要花多少费用才能完成装饰。
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】

4046

【思路】

考查图论中的最小生成树算法,好的我不会了(暂时还没学图论,说好的蓝桥杯省赛不考图论呢?)

【代码】

。。。。。。学了图论之后补上代码。。。。。。

编程大题

试题 F: 特殊年份

时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】
今年是 2021 年,2021 这个数字非常特殊,它的千位和十位相等,个位比
百位大 1,我们称满足这样条件的年份为特殊年份。
输入 5 个年份,请计算这里面有多少个特殊年份。
【输入格式】
输入 5 行,每行一个 4 位十进制数(数值范围为 1000 至 9999),表示一个
年份。
【输出格式】
输出一个整数,表示输入的 5 个年份中有多少个特殊年份。
【样例输入】
2019
2021
1920
2120
9899
【样例输出】
2
【样例说明】
2021 和 9899 是特殊年份,其它不是特殊年份。

【思路】

简单的取位+判断即可

【代码】

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int T = 5, ans = 0;
		while(T-- > 0)
		{
			int n = in.nextInt();
			int q = n / 1000 % 10;
			int b = n / 100 % 10;
			int s = n / 10 % 10;
			int g = n % 10;
			if(q == s && g - 1 == b)
			{
				ans++;
			}
		}
		System.out.println(ans);
		
		in.close();
	}

}

试题 G: 小平方

时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
小蓝发现,对于一个正整数 n 和一个小于 n 的正整数 v,将 v 平方后对 n
取余可能小于 n 的一半,也可能大于等于 n 的一半。
请问,在 1 到 n − 1 中,有多少个数平方后除以 n 的余数小于 n 的一半。
例如,当 n = 4 时,1, 2, 3 的平方除以 4 的余数都小于 4 的一半。
又如,当 n = 5 时,1, 4 的平方除以 5 的余数都是 1,小于 5 的一半。而
2, 3 的平方除以 5 的余数都是 4,大于等于 5 的一半。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一个整数,表示满足条件的数的数量。
【样例输入】
5
【样例输出】
2
【评测用例规模与约定】
对于所有评测用例,1 ≤ n ≤ 10000。

【思路】

简单的枚举题,但是要尤其注意比较时的除法精度问题,if((i * i) % n < (float)n / 2),不要少了(float)强制类型转换

【代码】

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int ans = 0;
		for(int i = 1; i <= n - 1; i++)
		{
			if((i * i) % n < (float)n / 2)  //注意精度问题!!!
			{
				ans++;
			}
		}
		System.out.println(ans);
		
		in.close();
	}

}

试题 H: 完全平方数

时间限制: 2.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
一个整数 a 是一个完全平方数,是指它是某一个整数的平方,即存在一个
整数 b,使得 a = b2。
给定一个正整数 n,请找到最小的正整数 x,使得它们的乘积是一个完全平
方数。
【输入格式】
输入一行包含一个正整数 n。
【输出格式】
输出找到的最小的正整数 x。
【样例输入 1】
12
【样例输出 1】 3
【样例输入 2】
15
【样例输出 2】
15
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 1000,答案不超过 1000。
对于 60% 的评测用例,1 ≤ n ≤ 10^8,答案不超过 10^8。
对于所有评测用例,1 ≤ n ≤ 10^12,答案不超过 10^12。

【思路】

判断x是不是完全平方数一个最朴素直接的办法是开根号之后强制转换为整型数据,砍掉后面的小数点,然后平方,看是否等于x。

这里要找出最小的i,使得i*n为一个完全平方数,那么从1枚举到n即可(最坏不会超过n了,自身的平方肯定是一个完全平方数)。

当然,该题的数据范围来看,这种解法会有个别数据样例超时,满分的解法是利用完全平方数的性质,分解质因数。

完全平方数的性质:除 1 外,一个完全平方数分解质因数后,各个质因数的指数都是偶数,如果一个数质分解后, 各个指数都为偶数, 那么它肯定是个平方数。 完全平方数的所有因数的总个数是奇数个。因数个数为奇数的自然数一定是完全平方数。

这里我们先提供最朴素方法的代码。

【代码】

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		long n = in.nextLong();
		for(long i = 1; i <= n; i++)
		{
			if((long)Math.sqrt(i * n) * (long)Math.sqrt(i * n) == i * n)
			{
				System.out.println(i);
				System.exit(0);
			}
		}
		
		in.close();
	}

}

试题 I: 负载均衡

时间限制: 2.0s 内存限制: 512.0MB 本题总分:25 分
【问题描述】
有 n 台计算机,第 i 台计算机的运算能力为 vi。
有一系列的任务被指派到各个计算机上,第 i 个任务在 ai 时刻分配,指定
计算机编号为 bi ,耗时为 ci 且算力消耗为 di 。如果此任务成功分配,将立刻
开始运行,期间持续占用 bi 号计算机 di 的算力,持续 ci 秒。
对于每次任务分配,如果计算机剩余的运算能力不足则输出 −1,并取消这
次分配,否则输出分配完这个任务后这台计算机的剩余运算能力。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示计算机数目和要分配的任务数。
第二行包含 n 个整数 v1, v2, · · · vn,分别表示每个计算机的运算能力。
接下来 m 行每行 4 个整数 ai, bi, ci, di,意义如上所述。数据保证 ai 严格递
增,即 ai < ai+1。
【输出格式】
输出 m 行,每行包含一个数,对应每次任务分配的结果。
【样例输入】
2 6
5 5
1 1 5 3
2 2 2 6
3 1 2 3
4 1 6 1
5 1 3 3
6 1 3 4
【样例输出】
2
-1
-1
1
-1
0
【样例说明】
时刻 1,第 1 个任务被分配到第 1 台计算机,耗时为 5 ,这个任务时刻 6
会结束,占用计算机 1 的算力 3。
时刻 2,第 2 个任务需要的算力不足,所以分配失败了。
时刻 3,第 1 个计算机仍然正在计算第 1 个任务,剩余算力不足 3,所以
失败。
时刻 4,第 1 个计算机仍然正在计算第 1 个任务,但剩余算力足够,分配
后剩余算力 1。
时刻 5,第 1 个计算机仍然正在计算第 1, 4 个任务,剩余算力不足 4,失
败。
时刻 6,第 1 个计算机仍然正在计算第 4 个任务,剩余算力足够,且恰好
用完。
【评测用例规模与约定】
对于 20% 的评测用例,n, m ≤ 200。
对于 40% 的评测用例,n, m ≤ 2000。
对于所有评测用例,1 ≤ n, m ≤ 200000,1 ≤ ai, ci, di, vi ≤ 10^9,1 ≤ bi ≤ n。

【思路】

首先搞懂题目意思,建议在纸上模拟一遍过程。

模拟,开辟一个二维数组T,T[i][j]存放的是i号计算机在时刻T还剩余多少算力。

但是,,,,这个朴素的方法显然不会是拿到满分的最优解,要能够处理题目要求的最大数据,数组要开到200005 * 1000000005,先别说题目的内存限制了,这么大的数组根本不能开辟(爆内存)。

所以我的数组只开到了10005 * 10005,大概只能过40%的数据吧。。。。。(将暴力骗分进行到底)

据说正确解法应该是使用栈。

【代码】

import java.util.Scanner;

public class Main {

	public static int[][] T = new int[10005][10005]; //T[i][j]存储的是i号计算机在时刻j还剩余多少算力
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();  //计算机台数
		int m = in.nextInt();  //要分配的任务数
		//输入每台计算机的算力
		for(int i = 1; i <= n; i++)
		{
			int cacl = in.nextInt();
			//初始化存放各计算机当前剩余算力的数组T
			for(int j = 1; j <= 5000; j++)
			{
				T[i][j] = cacl;
			}
		}
		//输入m次任务分配详情,并判断本次分配结果
		while(m-- > 0)
		{
			int ai = in.nextInt();  //时刻ai
			int bi = in.nextInt();  //分配给几号计算机
			int ci = in.nextInt();  //任务占用耗时
			int di = in.nextInt();  //任务消耗的算力
			if(T[bi][ai] < di)  //算力不够,无法分配,取消该次分配
			{
				System.out.println(-1);
				continue;
			}
			for(int j = ai; j < ai + ci; j++)  //占用时长为ci,消耗算力di
			{
				T[bi][j] -= di;
			}
			System.out.println(T[bi][ai]);
		}
		
		in.close();
	}

}

试题 J: 国际象棋

时间限制: 3.0s 内存限制: 512.0MB 本题总分:25 分
【问题描述】
众所周知,“八皇后” 问题是求解在国际象棋棋盘上摆放 8 个皇后,使得两
两之间互不攻击的方案数。已经学习了很多算法的小蓝觉得 “八皇后” 问题太简
单了,意犹未尽。作为一个国际象棋迷,他想研究在 N × M 的棋盘上,摆放 K
个马,使得两两之间互不攻击有多少种摆放方案。由于方案数可能很大,只需
计算答案除以 1000000007 (即 10^9 + 7) 的余数。
如下图所示,国际象棋中的马摆放在棋盘的方格内,走 “日” 字,位于 (x, y)
格的马(第 x 行第 y 列)可以攻击 (x + 1, y + 2)、(x + 1, y − 2)、(x − 1, y + 2)、 (x − 1, y − 2)、(x + 2, y + 1)、(x + 2, y − 1)、(x − 2, y + 1) 和 (x − 2, y − 1) 共 8 个
格子。


【输入格式】
输入一行包含三个正整数 N, M, K,分别表示棋盘的行数、列数和马的个
数。
试题 J: 国际象棋 13
第十二届蓝桥杯大赛软件赛省赛第二场 Java 大学 B 组
【输出格式】
输出一个整数,表示摆放的方案数除以 1000000007 (即 109 + 7) 的余数。
【样例输入】
1 2 1
【样例输出】
2
【样例输入】
4 4 3
【样例输出】
276
【样例输入】
3 20 12
【样例输出】
914051446
【评测用例规模与约定】
对于 5% 的评测用例,K = 1;
对于另外 10% 的评测用例,K = 2;
对于另外 10% 的评测用例,N = 1;
对于另外 20% 的评测用例,N, M ≤ 6,K ≤ 5;
对于另外 25% 的评测用例,N ≤ 3,M ≤ 20,K ≤ 12;
对于所有评测用例,1 ≤ N ≤ 6,1 ≤ M ≤ 100,1 ≤ K ≤ 20。

【思路】

看到题目果断深度优先搜索+回溯。开辟一个二维数组记录哪些位置有棋子,再开辟一个二维数组记录哪些位置会被攻击。递归下探,每次下探到一个格子的时候,有放和不放两种决策,放的话要保证该位置不会被攻击,且该棋子攻击范围内也没有其他棋子(检查数组标记即可),做好标记,然后递归下探尝试下一个格子。直到成功放下K个棋子或者整个棋盘尝试完毕,递归返回时要进行回溯(即把标记取消)。

为节省代码量,开辟一个增量数组,存放可以攻击到的位置的坐标增量(地图、棋盘类深搜题基本都可以用这个小技巧),攻击位置的标记和取消可以专门封装成函数。

不过。。。。这个方法复杂度大约为O(2^(mn)),只能过部分的测试数据,稍大的数据便会超时,大一萌新一只,压轴题能捞到分已经不容易了。。。还是太菜了点。

看了其他大佬的题解,正解应该是状压DP。

【代码】

import java.util.Scanner;

public class Main {

	public static int n, m, K;  //在n行m列的棋盘上摆放k个马
	public static int[][] rec = new int[10][105];  //记录哪些位置有棋子了
	public static int[][] attack = new int[10][105];  //标记哪些位置会被攻击
	public static int[][] direction = new int[][] {  //位移增量数组(可以攻击到的位置)
		{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}
	};
	public static int ans= 0;
	public static int mod = 1000000007;
	
	public static void dfs(int row, int col, int k) {
		if(k == K)  //成功放下了K个棋子
		{
			ans++;
			return;
		}
		if(row == n)  //到达边界
		{
			return;
		}
		
		if(attack[row][col] == 0 && check(row, col))  //该位置不受攻击且不会攻击到其他的棋子
		{
			//标记
			rec[row][col] = 1;  //标记该位置放了一个棋子
			attack(row, col);  //标记会被该棋子攻击到的位置
			//下探,开始尝试下一个位置
			dfs(row + ((col + 1) / m), (col + 1) % m, k + 1);
			//回溯
			rec[row][col] = 0;
			cancel(row, col);
		}
		//该位置不放,开始尝试下一个位置
		dfs(row + ((col + 1) / m), (col + 1) % m, k);  //这样写可实现自动换行
	}
	
	public static void attack(int row, int col) {  //标记被攻击的位置
		for(int i = 0; i < 8; i++)
		{
			int nrow = row + direction[i][0];
			int ncol = col + direction[i][1];
			if(nrow >= 0 && nrow < n && ncol >= 0 && ncol < m)
			{
				attack[nrow][ncol] = 1;
			}
		}
	}
	
	public static void cancel(int row, int col) {  //用于回溯时取消被攻击位置的标记
		for(int i = 0; i < 8; i++)
		{
			int nrow = row + direction[i][0];
			int ncol = col + direction[i][1];
			if(nrow >= 0 && nrow < n && ncol >= 0 && ncol < m)
			{
				attack[nrow][ncol] = 0;
			}
		}
	}
	
	public static boolean check(int row, int col) {  //检查该位置上的棋子攻击范围内是否没有其他棋子
		for(int i = 0; i < 8; i++)
		{
			int nrow = row + direction[i][0];
			int ncol = col + direction[i][1];
			if(nrow >= 0 && nrow < n && ncol >= 0 && ncol < m && rec[nrow][ncol] == 1)
			{
				return false;
			}
		}
		return true;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		K = in.nextInt();
		dfs(0, 0, 0);
		System.out.println(ans);
		
		in.close();
	}

}

小结

比赛之前也做了今年第一场的题目,感觉比往年要难不少,第二场的题个人感觉还是比第一场稍微简单一些的,大一萌新,第一次参加蓝桥杯,发挥的还算可以,拿到了省一,但在省一当中比较靠后(看来对手们都比较强啊),国赛要加把劲儿了。水平不高,要学的还有很多,欢迎各位热爱编程的大佬一起交流讨论。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值