草庐IT

2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第2场省赛 2021.05.09】

延锋L 2023-04-04 原文

  1. 2013年 第04届 蓝桥杯 Java B组 省赛真题详解及小结
  2. 2014年 第05届 蓝桥杯 Java B组 省赛真题详解及小结
  3. 2015年 第06届 蓝桥杯 Java B组 省赛真题详解及小结
  4. 2016年 第07届 蓝桥杯 Java B组 省赛真题详解及小结
  5. 2017年 第08届 蓝桥杯 Java B组 省赛真题详解及小结
  6. 2018年 第09届 蓝桥杯 Java B组 省赛真题详解及小结
  7. 2019年 第10届 蓝桥杯 Java B组 省赛真题详解及小结
  8. 2020年 第11届 蓝桥杯 第1次模拟赛真题详解及小结【Java版】(校内模拟)// 官方讲解视频
  9. 2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】// 官方讲解视频
  10. 2020年 第11届 蓝桥杯 C/C++ B组 省赛真题详解及小结【第1场省赛 2020.07.05】【Java版】
  11. 2020年 第11届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2020.07.05】
  12. 2020年 第11届 蓝桥杯 Java B组 省赛真题详解及小结【第2场省赛 2020.10.17】
  13. 2020年 第11届 蓝桥杯 Java C组 省赛真题详解及小结【第1场省赛 2020.07.05】
  14. 2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2021.04.18】
  15. 2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第2场省赛 2021.05.09】
  16. 2022年 第13届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2022.04.09】

  1. 2015年 第06届 蓝桥杯 Java B组 决赛真题详解及小结
  2. 2016年 第07届 蓝桥杯 Java B组 决赛真题详解及小结
  3. 2017年 第08届 蓝桥杯 Java B组 决赛真题详解及小结
  4. 2018年 第09届 蓝桥杯 Java B组 决赛真题详解及小结
  5. 2019年 第10届 蓝桥杯 Java B组 决赛真题详解及小结
  6. 2020年 第11届 蓝桥杯 Java B组 决赛真题详解及小结【2020.11.14】
  7. 2021年 第12届 蓝桥杯 Java B组 决赛真题详解及小结【2021.06.05】
  8. 2022年 第13届 蓝桥杯 Java B组 决赛真题详解及小结【2022.00.00】

目录

一、试题A:求余(本题总分:5 分)

01解法一:直接计算

二、试题B:双阶乘(本题总分:5 分)

02解法一

02解法二:简单枚举

三、试题C:格点(本题总分:10 分)

03解法一:简单枚举

03解法二:朴素枚举

03解法三:稍微优化

四、试题D:整数分解(本题总分:10 分)

04解法一:排列组合

04解法二:组合公式

04解法三:动态规划

五、试题E:城邦(本题总分:15 分)

05解法一:最小生成树-Kruskal

05解法二:最小生成树-Prim

六、试题F:特殊年份(时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分)

06解法一:朴素解法

06解法二:BCD码

06解法三:取位判断

七、试题G:小平方(时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分)

07解法一:朴素解法

07解法二:简单枚举

八、试题H:完全平方数(时间限制: 2.0s 内存限制: 512.0MB 本题总分:20 分)

08解法一:简单数学

08解法二:朴素解法

九、试题I:负载均衡(时间限制: 2.0s 内存限制: 512.0MB 本题总分:25 分)

09解法一:优先队列模拟

09解法二:暴力求解

十、试题J:国际象棋(时间限制: 3.0s 内存限制: 512.0MB 本题总分:25 分)

10解法一:状压DP

10解法二

小结


参考博客:

  1. 第十二届蓝桥杯 2021年省赛真题 (Java 大学B组) 第二场_NOW I GOT ONE WAY-CSDN博客
  2. 第十二届蓝桥杯省赛第二场 Java B组真题个人题解_bughunter-的博客-CSDN博客

仅供参考,欢迎指正!部分为个人想法和解题思路,如有错误或不足,欢迎指正。

 

一、试题A:求余(本题总分:5 分)

本题总分:5 分

【问题描述】

        在 C/C++/Java/Python 等语言中,使用 % 表示求余,请问 2021%20 的值是多少?

【答案提交】

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

【答案】:1

01解法一:直接计算

package provincialGames_12_2021_2_JavaB;

public class A01_求余 { // 1
	public static void main(String[] args) {
		System.out.println(2021%20); // 1
	}
}

二、试题B:双阶乘(本题总分:5 分)

本题总分: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

02解法一

( a × b ) % p = ( a % p × b % p ) % p ,p ∈ Z

package provincialGames_12_2021_2_JavaB;

public class B02_双阶乘 { // 59375
	public static void main(String[] args) {
		new B02_双阶乘().run();
	}

	void run() {
		int n = 2021 + 2, ans = 1;
		while (n > 1)
			ans = (ans * (n -= 2)) % 100000;
		System.out.println(ans); // 59375
	}
}

02解法二:简单枚举

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

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class B02_双阶乘2 { // 59375
	public static void main(String[] args) {
		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); // 59375
		in.close();
	}
}

三、试题C:格点(本题总分:10 分)

本题总分:10 分

【问题描述】

        如果一个点 (x, y) 的两维坐标都是整数,即 x ∈ Z 且 y ∈ Z,则称这个点为一个格点。

        如果一个点 (x, y) 的两维坐标都是正数,即 x > 0 且 y > 0,则称这个点在第一象限。

        请问在第一象限的格点中,有多少个点 (x, y) 的两维坐标乘积不超过 2021,即 x · y ≤ 2021。

        提示:建议使用计算机编程解决问题。

【答案提交】

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

【答案】:15698

03解法一:简单枚举

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

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class C03_格点 { // 15698
	public static void main(String[] args) {
		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); // 15698
		in.close();
	}
}

03解法二:朴素枚举

package provincialGames_12_2021_2_JavaB;

public class C03_格点2 { // 15698
	public static void main(String[] args) {
		new C03_格点2().run();
	}

	int n = 2021;

	void run() {
		int ans = 0;
		for (int x = 1; x <= n; x++)
			for (int y = 1; y <= n; y++)
				if (x * y <= n)
					ans++;
		System.out.println(ans); // 15698
	}
}

03解法三:稍微优化

【解析】:题目解读一下就是多少个正整数二元组的乘积等于 k,k∈[1,2021],我们可以用欧拉筛线性的分解出区间内每个数的质因数,用其质数计算出组合方案的种数。

package provincialGames_12_2021_2_JavaB;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class C03_格点3 { // 15698
	public static void main(String[] args) {
		new C03_格点3().run();
	}

	int n = 2021, ans = 0;

	void run() {
		Map<Integer, Integer>[] nums = new Map[n + 1];
		nums[1] = new HashMap() {
			{
				put(0, 0);
			}
		};
		List<Integer> primes = new ArrayList();
		for (int i = 2; i <= n; i++) {
			if (nums[i] == null) {
				(nums[i] = new HashMap()).put(i, 1);
				primes.add(i);
			}
			for (int p : primes) {
				if (i * p > n)
					break;
				nums[i * p] = new HashMap(nums[i]);
				nums[i * p].put(p, nums[i].getOrDefault(p, 0) + 1);
				if (i % p == 0)
					break;
			}
		}
		for (int i = 1; i <= n; i++) {
			int count = 1;
			for (int k : nums[i].values())
				count *= k + 1;
			ans += count;
		}
		System.out.println(ans); // 15698
	}
}

四、试题D:整数分解(本题总分:10 分)

本题总分: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

04解法一:排列组合

【解析】:暴力破解的话,2021的五次方几个小时都运行不出结果,一种可行的办法是使用数学中排列组合公式算出结果。数学方法,排列组合。

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

package provincialGames_12_2021_2_JavaB;

public class D04_整数分解 { // 691677274345
	public static void main(String[] args) {
		new D04_整数分解().run();
	}

	int n = 2021;

	void run() {
		long ans = 0;
		for (int k = 1, a, b; k <= n - 2 - 2; k++) {
			for (a = 2, b = n - 2 - k; b >= 2; a++, b--)
				ans += (a - 1) * (b - 1);
		}
		System.out.println(ans); // 691677274345
	}
}

04解法二:组合公式

package provincialGames_12_2021_2_JavaB;

public class D04_整数分解2 { // 691677274345
	public static void main(String[] args) {
		new D04_整数分解2().run();
	}

	long n = 2021;

	void run() {
		System.out.println((n - 1) * (n - 2) * (n - 3) * (n - 4) / 4 / 3 / 2);
	}
}

04解法三:动态规划

package provincialGames_12_2021_2_JavaB;

import java.util.Arrays;

public class D04_整数分解3 { // 691677274345
	public static void main(String[] args) {
		new D04_整数分解3().run();
	}

	int n = 2021, k = 5;

	void run() {
		long[][] dp = new long[k + 1][n + 1];
		Arrays.fill(dp[1], 1);
		for (int i = 2; i <= k; i++)
			for (int j = i; j <= n; j++)
				dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
		System.out.println(dp[k][n]); // 691677274345
	}
}

五、试题E:城邦(本题总分:15 分)

本题总分:15 分

【问题描述】

        小蓝国是一个水上王国,有 2021 个城邦,依次编号 1 到 2021。在任意两个城邦之间,都有一座桥直接连接。

        为了庆祝小蓝国的传统节日,小蓝国政府准备将一部分桥装饰起来。

        对于编号为 a 和 b 的两个城邦,它们之间的桥如果要装饰起来,需要的费用如下计算:找到 a 和 b 在十进制下所有不同的数位,将数位上的数字求和。

        例如,编号为 2021 和 922 两个城邦之间,千位、百位和个位都不同,将这些数位上的数字加起来是 (2 + 0 + 1) + (0 + 9 + 2) = 14。注意 922 没有千位,千位看成 0。

        为了节约开支,小蓝国政府准备只装饰 2020 座桥,并且要保证从任意一个城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。

        请问,小蓝国政府至少要花多少费用才能完成装饰。

        提示:建议使用计算机编程解决问题。

【答案提交】

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

【答案】:4046

05解法一:最小生成树-Kruskal

package provincialGames_12_2021_2_JavaB;

import java.util.ArrayList;
import java.util.List;

public class E05_城邦 {
	public static void main(String[] args) {
		new E05_城邦().run();
	}

	int n = 2021;

	int[] link = new int[n + 1];

	void run() {
		List<Edge> edges = new ArrayList();
		for (int v = 1; v < n; v++) {
			link[v] = v;
			for (int w = v + 1; w <= n; w++)
				edges.add(new Edge(v, w, calc(v, w)));
		}
		edges.sort((e1, e2) -> (e1.weight - e2.weight));
		int ans = 0, k = 1;
		for (Edge edge : edges) {
			int v = find(edge.v);
			int w = find(edge.w);
			if (v != w) {
				link[v] = w;
				ans += edge.weight;
				if (++k == n)
					break;
			}
		}
		System.out.println(ans);
	}

	int find(int k) {
		return k == link[k] ? k : (link[k] = find(link[k]));
	}

	int calc(int v, int w) {
		int res = 0;
		for (int i = 0; i < 4; i++) {
			if (v % 10 != w % 10)
				res += v % 10 + w % 10;
			v /= 10;
			w /= 10;
		}
		return res;
	}

	class Edge {
		int v, w, weight;

		Edge(int v, int w, int weight) {
			this.v = v;
			this.w = w;
			this.weight = weight;
		}
	}
}

05解法二:最小生成树-Prim

package provincialGames_12_2021_2_JavaB;

import java.util.Arrays;

public class E05_城邦2 {
	public static void main(String[] args) {
		new E05_城邦2().run();
	}

	int n = 2021;

	void run() {
		boolean[] marked = new boolean[n + 1];
		int[] dist = new int[n + 1];
		Arrays.fill(dist, 0x7FFFFFFF);
		int ans = 0;
		dist[1] = 0;
		for (int k = 0; k < n; k++) {
			int V = 0;
			for (int i = 1; i <= n; i++)
				if (!marked[i] && dist[i] < dist[V])
					V = i;
			marked[V] = true;
			ans += dist[V];
			for (int i = 1; i <= n; i++)
				if (!marked[i])
					dist[i] = min(dist[i], calc(i, V));
		}
		System.out.println(ans);
	}

	int min(int a, int b) {
		return a < b ? a : b;
	}

	int calc(int v, int w) {
		int res = 0;
		for (int i = 0; i < 4; i++) {
			if (v % 10 != w % 10)
				res += v % 10 + w % 10;
			v /= 10;
			w /= 10;
		}
		return res;
	}
}

六、试题F:特殊年份(时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分)

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

【问题描述】

        今年是 2021 年,2021 这个数字非常特殊,它的千位和十位相等,个位比百位大 1,我们称满足这样条件的年份为特殊年份。

        输入 5 个年份,请计算这里面有多少个特殊年份。

【输入格式】输入 5 行,每行一个 4 位十进制数(数值范围为 1000 至 9999),表示一个年份。

【输出格式】输出一个整数,表示输入的 5 个年份中有多少个特殊年份。

【样例输入】2019 2021 1920 2120 9899

【样例输出】2

【样例说明】2021 和 9899 是特殊年份,其它不是特殊年份。

06解法一:朴素解法

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class F06_特殊年份 {
	public static void main(String[] args) {
		new F06_特殊年份().run();
	}

	void run() {
		Scanner in = new Scanner(System.in);
		int ans = 0, k = 5;
		while (k-- > 0)
			if (check(in.nextInt()))
				ans++;
		System.out.println(ans);
	}

	boolean check(int year) {
		return (year / 1000 == (year % 100) / 10) && ((year % 10) - 1 == (year % 1000) / 100);
	}
}

06解法二:BCD码

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class F06_特殊年份2 {
	public static void main(String[] args) {
		new F06_特殊年份2().run();
	}

	void run() {
		Scanner in = new Scanner(System.in);
		int ans = 0, k = 5;
		while (k-- > 0)
			if (check(in.nextInt(16)))
				ans++;
		System.out.println(ans);
	}

	boolean check(int year) {
		return (year >> 12 == ((year >> 4) & 0xf)) && ((year & 0xf) - 1 == ((year >> 8) & 0xf));
	}
}

06解法三:取位判断

【解析】:简单的取位+判断即可。

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class F06_特殊年份3 {
	public static void main(String[] args) {
		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 分)

时间限制: 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。

07解法一:朴素解法

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class G07_小平方 {
	public static void main(String[] args) {
		new G07_小平方().run();
	}

	void run() {
		int n = new Scanner(System.in).nextInt(), ans = 0;
		for (int i = 1; i < n; i++)
			if (i * i % n < n + 1 >> 1)
				ans++;
		System.out.println(ans);
	}
}

07解法二:简单枚举

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

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class G07_小平方2 {
	public static void main(String[] args) {
		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 分)

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

【问题描述】

        一个整数 a 是一个完全平方数,是指它是某一个整数的平方,即存在一个整数 b,使得 a = b^2。

        给定一个正整数 n,请找到最小的正整数 x,使得它们的乘积是一个完全平方数。

【输入格式】输入一行包含一个正整数 n。

【输出格式】输出找到的最小的正整数 x。

【样例输入 1】12

【样例输出 1】3

【样例输入 2】15

【样例输出 2】15

【评测用例规模与约定】

        对于 30% 的评测用例,1 ≤ n ≤ 1000,答案不超过 1000。

        对于 60% 的评测用例,1 ≤ n ≤ 108,答案不超过 10^8。

        对于所有评测用例,1 ≤ n ≤ 1012,答案不超过 10^12。

08解法一:简单数学

一个完全平方数,它的质因子的指数总是个偶数。

因此,对给定的 n 分解质因数,将指数为奇数的质因子累乘起来,得到的积就是我们要找的x。

特别地,我们需要将枚举的范围缩小至,以规避n为极大质数的情况。

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class H08_完全平方数 {
	public static void main(String[] args) {
		new H08_完全平方数().run();
	}

	void run() {
		long n = new Scanner(System.in).nextLong();
		long ans = 1;
		for (long k = 2; k * k <= n; k++)
			if (n % k == 0) {
				int exp = 0;
				while (n % k == 0) {
					n /= k;
					exp++;
				}
				if (exp % 2 == 1)
					ans *= k;
			}
		System.out.println(n == 1 ? ans : n * ans);
	}
}

08解法二:朴素解法

判断x是不是完全平方数一个最朴素直接的办法是开根号之后强制转换为整型数据,砍掉后面的小数点,然后平方,看是否等于x。
这里要找出最小的i,使得i*n为一个完全平方数,那么从1枚举到n即可(最坏不会超过n了,自身的平方肯定是一个完全平方数)。
当然,该题的数据范围来看,这种解法会有个别数据样例超时,满分的解法是利用完全平方数的性质,分解质因数。
完全平方数的性质:除 1 外,一个完全平方数分解质因数后,各个质因数的指数都是偶数,如果一个数质分解后, 各个指数都为偶数, 那么它肯定是个平方数。完全平方数的所有因数的总个数是奇数个。因数个数为奇数的自然数一定是完全平方数。
这里我们先提供最朴素方法的代码。

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class H08_完全平方数2 {
	public static void main(String[] args) {
		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 分)

时间限制: 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。

09解法一:优先队列模拟

以为是个Big Implementation,构思完后发现代码量还蛮少的。
直接用优先队列来模拟每个计算机正在运行的任务,当当前计算机即将可能被分配任务时,清除队列中已经被执行完的任务,并归还算力即可。看到那种输入有各种各样的变量的题,基本都是模拟。

package provincialGames_12_2021_2_JavaB;

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

public class I09_负载均衡 {
	public static void main(String[] args) {
		new I09_负载均衡().run();
	}

	void run() {
		InputReader in = new InputReader(System.in);
		PrintWriter out = new PrintWriter(System.out);
		int n = in.readInt(), m = in.readInt();
		Queue<Item>[] cpt = new Queue[n + 1];
		int[] cptd = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			cpt[i] = new PriorityQueue();
			cptd[i] = in.readInt();
		}
		while (m-- > 0) {
			int a = in.readInt();
			int b = in.readInt();
			int c = in.readInt();
			int d = in.readInt();
			while (cpt[b].size() > 0 && cpt[b].peek().c <= a)
				cptd[b] += cpt[b].poll().d;
			if (cptd[b] >= d) {
				cpt[b].offer(new Item(a + c, d));
				out.println(cptd[b] -= d);
			} else
				out.println("-1");
		}
		out.flush();
	}

	class Item implements Comparable<Item> {
		int c, d;

		Item(int c, int d) {
			this.c = c;
			this.d = d;
		}

		public int compareTo(Item item) {
			return this.c - item.c;
		}
	}

	class InputReader {
		BufferedReader reader;
		StringTokenizer token;

		InputReader(InputStream in) {
			this.reader = new BufferedReader(new InputStreamReader(in));
		}

		String read() {
			while (token == null || !token.hasMoreTokens()) {
				try {
					token = new StringTokenizer(reader.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return token.nextToken();
		}

		int readInt() {
			return Integer.parseInt(read());
		}
	}
}

09解法二:暴力求解

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

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

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

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

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

package provincialGames_12_2021_2_JavaB;

import java.util.*;

public class I09_负载均衡2 {
	public static int[][] T = new int[10005][10005]; // T[i][j]存储的是i号计算机在时刻j还剩余多少算力

	public static void main(String[] args) {
		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 分)

时间限制: 3.0s 内存限制: 512.0MB 本题总分:25 分

【问题描述】

        众所周知,“八皇后” 问题是求解在国际象棋棋盘上摆放 8 个皇后,使得两两之间互不攻击的方案数。已经学习了很多算法的小蓝觉得 “八皇后” 问题太简单了,意犹未尽。作为一个国际象棋迷,他想研究在 N × M 的棋盘上,摆放 K 个马,使得两两之间互不攻击有多少种摆放方案。由于方案数可能很大,只需计算答案除以 1000000007 (即 109 + 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,分别表示棋盘的行数、列数和马的个数。

【输出格式】输出一个整数,表示摆放的方案数除以 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。

10解法一:状压DP

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class J10_国际象棋 {
	public static void main(String[] args) {
		new J10_国际象棋().run();
	}

	final int mod = 1000000007;

	void run() {
		Scanner in = new Scanner(System.in);
		int N = 1 << in.nextInt(), M = in.nextInt(), K = in.nextInt();
		int[][][][] dp = new int[M + 1][N][N][K + 1];
		dp[0][0][0][0] = 1;
		for (int m = 1; m <= M; m++)
			for (int now = 0; now < N; now++)
				for (int pr1 = 0; pr1 < N; pr1++)
					if ((now << 2 & pr1) == 0 && (pr1 << 2 & now) == 0)
						for (int pr2 = 0; pr2 < N; pr2++)
							if ((now << 1 & pr2) == 0 && (pr2 << 1 & now) == 0)
								for (int k = bitCount(now), g = k; k <= K; k++)
									dp[m][now][pr1][k] = (dp[m][now][pr1][k] + dp[m - 1][pr1][pr2][k - g]) % mod;
		int ans = 0;
		for (int now = 0; now < N; now++)
			for (int pr1 = 0; pr1 < N; pr1++)
				if ((now << 2 & pr1) == 0 && (pr1 << 2 & now) == 0)
					ans = (ans + dp[M][now][pr1][K]) % mod;
		System.out.println(ans);
	}

	public static int bitCount(int n) {
		n = n - ((n >>> 1) & 0x55555555);
		n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
		n = (n + (n >>> 4)) & 0x0f0f0f0f;
		n = n + (n >>> 8);
		n = n + (n >>> 16);
		return n & 0x3f;
	}
}

10解法二

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

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

不过。。。。这个方法复杂度大约为O(2^(mn)),只能过部分的测试数据,稍大的数据便会超时。大佬思路:状压DP。

package provincialGames_12_2021_2_JavaB;

import java.util.Scanner;

public class J10_国际象棋2 {
	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) {
		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();
	}
}

小结

好好学习~

有关2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第2场省赛 2021.05.09】的更多相关文章

  1. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  2. 华为OD机试真题 C++ 实现【带传送阵的矩阵游离】【2023 Q2 | 200分】 - 2

            所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。

  3. 蓝桥杯备赛(二) - 2

    目录前言: 一、ASC分析代码实现二、 卡片分析代码实现三、 直线分析代码实现四、货物摆放分析代码实现小结:前言:  在刷题的过程中,发现蓝桥杯的题目和力扣的差别很大。让人有一种不一样的感觉,蓝桥杯题目偏向对于实际问题用编程去的解决,而力扣给人感觉很锻炼自己的编程思维,逻辑能力。两者结合去刷,相信会有不一样的收获。 一、ASC  已知大写字母A的ASCII码为65,请问大写字母L的ASCII码是多少?分析  这道题目看上去很简单,我们需确定自己计算的准确,所以我建议用编程去解决。代码实现publicclassTest8{publicstaticvoidmain(String[]args){Sy

  4. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  5. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  6. ruby-on-rails - 无法构建 gem native 扩展 (mkmf (LoadError)) - Ubuntu 12.04 - 2

    这个问题在这里已经有了答案:Unabletoinstallgem-Failedtobuildgemnativeextension-cannotloadsuchfile--mkmf(LoadError)(17个答案)关闭9年前。嘿,我正在尝试在一台新的ubuntu机器上安装rails。我安装了ruby​​和rvm,但出现“无法构建gemnative扩展”错误。这是什么意思?$sudogeminstallrails-v3.2.9(没有sudo表示我没有权限)然后它会输出很多“获取”命令,最终会出现这个错误:Buildingnativeextensions.Thiscouldtakeawhi

  7. ruby - 使用 OpenSSL ruby​​ 从一个 .p12 文件中提取多个 key - 2

    我想知道如何从Apple.p12文件中提取key。根据我有限的理解,.p12文件是X504证书和私钥的组合。我看到我遇到的每个.p12文件都有一个X504证书和至少一个key,在某些情况下有两个key。这是因为每个.p12都有一个Apple开发人员key,有些还有一个额外的key(可能是Appleroot授权key)。我只考虑那些具有两个key的.p12文件是有效的。我的目标是区分具有一个key的.p12文件和具有两个key的.p12文件。到目前为止,我已经使用OpenSSL来检查X504文件和任何.p12的key。例如,我有这段代码可以检查目录中的所有.p12文件:Dir.glob(

  8. ruby - 为什么 openssl 在 windows 上产生错误但在 centos 上不产生错误:PKCS12_parse: mac verify failure (OpenSSL::PKCS12::PKCS12Error) - 2

    require'openssl'ifARGV.length==2pkcs12=OpenSSL::PKCS12.new(File.read(ARGV[0]),ARGV[1])ppkcs12.certificateelseputs"Usage:load_cert.rb"end运行它会在Windows上产生错误,但在Linux上不会。错误:OpenSSL::PKCS12::PKCS12Error:PKCS12_parse:macverifyfailurefrom(irb):21:ininitializefrom(irb):21:innewfrom(irb):21fromC:/Ruby192/

  9. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  10. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

随机推荐