2021年蓝桥杯Java-B组国赛
国赛6.18,差不多一个半月,还是可以好好准备下,拿个国三就行hhhh。
🍔A 整数范围
用 8 位二进制(一个字节)来表示一个非负整数,表示的最小值是0,则一般能表示的最大值是多少?
8位1 = 0xff = 0x100 - 1 = 16 * 16 - 1 = 255
答案:255
🍟B 纯质数
用线性筛模板,在筛出来的素数基础上,再逐位做check,注意0、1都不是质数!
答案:1903
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
// 1 - 20210605
// 线性筛模板
boolean[] isprime = new boolean[30000010];
isprime[1] = true;
isprime[0] = true;
int[] prime = new int[30000010];
int cnt = 0;
for (int i = 2; i <= 20210605; i++) {
if (isprime[i] == false) {
prime[cnt++] = i;
}
for (int j = 0; j < cnt; j++) {
if (prime[j] * i > 20210605) break;
isprime[prime[j] * i] = true;
if (i % prime[j] == 0) break;
}
}
int ans = 0;
// 遍历所有的质数,注意要把0、1置为true
for (int i = 0; i < cnt; i++) {
boolean flag = false;
int cur = prime[i];
while (cur != 0) {
if (isprime[cur % 10]) {
flag = true;
break;
}
cur /= 10;
}
if (flag == false) ans++;
}
System.out.println(ans);
}
}
🌭C 完全日期(时间模拟)
时间模拟问题最关键的是闰年的处理,然后就是逐日累加即可。
答案:977
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static boolean isLeap(int year) {
// (mod 4 = 0 && mod 100 != 0) || mod 400 = 0
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return true;
return false;
}
public static void main(String[] args) throws IOException {
boolean[] check = new boolean[10010];
for (int i = 1; i <= 100; i++) {
// 完全平方数打表
check[i * i] = true;
}
int[] M = new int[] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int year = 2001;
int month = 1;
int day = 1;
int ans = 0;
while (true) {
int cur = 0;
cur += getNum(year);
cur += getNum(month);
cur += getNum(day);
if (check[cur]) {
ans++;
}
// 按照天数进行更新
day++;
if (month == 2) {
if (isLeap(year)) {
if (day == 30) {
day = 1;
month++;
}
} else {
if (day == 29) {
day = 1;
month++;
}
}
} else {
if (day == M[month] + 1) {
day = 1;
month++;
}
}
if (month > 12) {
month = 1;
year++;
}
// System.out.println("year:" + year + "month:" + month + "day:" + day);
if (year == 2022) break;
}
System.out.println(ans);
}
static int getNum(int num) {
int cur = 0;
while (num != 0) {
cur += num % 10;
num /= 10;
}
return cur;
}
}
给出一种借助LocalDate实现的方法:
import java.time.LocalDate;
public class Test {
public static final int maxPerfect = 2 + 0 + 1 + 9 + 0 + 9 + 2 + 9;
public static final boolean[] perfect = new boolean[maxPerfect + 1];
public static LocalDate start = LocalDate.of(2001, 01, 01);
public static LocalDate end = LocalDate.of(2021, 12, 31);
public static void main(String[] args) {
int count = 0;
for (int i = 1; i * i<= maxPerfect; i++)
perfect[i * i] = true;
while (end.compareTo(start) >= 0) {
if (perfect[calc(start)])
count++;
start = start.plusDays(1);
}
System.out.println(count);
}
public static int calc(LocalDate date) {
String dateStr = date.toString();
int res = 0;
for (int i = dateStr.length() - 1; i >= 0; i--)
if (Character.isDigit(dateStr.charAt(i)))
res += Character.digit(dateStr.charAt(i), 10);
return res;
}
}
🍿D 最小权值(记忆化搜索、DP)
记忆化搜索
思考为什么会存在最小可能值?说明是二叉树的建树方案不唯一,所以需要遍历可能的建树方案(DFS建树),就是每次遍历左子树的节点数,根据总节点数确定出右子树的节点数。
为避免重复搜索,所以引入记忆化table进行记忆化搜索。
答案:2653631372
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static long[] table = new long[2022];
public static void main(String[] args) throws IOException {
System.out.println(dfs(2021));
}
static long dfs(int Tsize) {
if (Tsize == 0) return 0;
// 记忆化搜索
if (table[Tsize] != 0) return table[Tsize];
// 记录最小权值
long minWeight = Long.MAX_VALUE;
// 先遍历左子树节点, 最多到Tsize - 1,因为根节点需要1个节点
for (int LC = 0; LC < Tsize; LC++) {
// 确定右子树节点, -1同样因为根节点需要1个节点
int RC = Tsize - LC - 1;
long weight = 1 + 2 * dfs(LC) + 3 * dfs(RC) + LC * LC * RC;
// 遍历所有的可能构树情况,确定最小的权值
minWeight = Math.min(minWeight, weight);
}
// 记忆
table[Tsize] = minWeight;
return minWeight;
}
}
能用记忆化?那肯定可以转成dp问题!
就是把记忆化table,转成dp。
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static long[] table = new long[2022];
public static void main(String[] args) throws IOException {
long[] dp = new long[2022];
dp[1] = 1; // 只有一个节点的情况
// 遍历总节点数
for (int Tsize = 2; Tsize <= 2021; Tsize++) {
long minWeight = Long.MAX_VALUE;
// 遍历左子树的节点数
for (int LC = 0; LC < Tsize; LC++) {
// 确定右子树的节点数
int RC = Tsize - LC - 1;
long weight = 1 + 2 * dp[LC] + 3 * dp[RC] + LC * LC * RC;
minWeight = Math.min(minWeight, weight);
}
dp[Tsize] = minWeight;
}
System.out.println(dp[2021]);
}
}
🍕E 大写
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
String input = reader.readLine().trim();
input = input.toUpperCase(Locale.ROOT);
writer.write(input);
writer.flush();
}
}
🥓F 123
只会最简单的模拟,记录所有查询中最大的下标,然后去构造同样长度的序列,再求数组的累加和。(纯正的模拟)
转换为前缀和来做
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
int n = Integer.parseInt(reader.readLine().trim());
while (n-- > 0) {
String[] input = reader.readLine().trim().split(" ");
long left = Long.parseLong(input[0]);
long right = Long.parseLong(input[1]);
// 转换为前缀和
long ans = calSum(right) - calSum(left - 1);
writer.write(ans + "\n");
}
writer.flush();
}
static long calSum(long idx) {
long ans = 0;
long cnt = 1;
long cur = 1;
while (cur <= idx) {
ans += cnt * (cnt + 1) / 2;
// 新下标的数据长度
cnt++;
// 下标移动
cur += cnt;
}
// 最好的情况是cur == idx
// 但大部分情况是超过idx
if (cur > idx) {
cur -= cnt;
cnt = idx - cur;
ans += cnt * (cnt + 1) / 2;
}
return ans;
}
}
🍗G 和与乘积
很不错很不错,只能暴力偏分。
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int n = Integer.parseInt(input[0]);
input = reader.readLine().trim().split(" ");
int[] num = new int[n];
// 前缀和
long[] preSum = new long[n + 1];
for (int i = 0; i < n; i++) {
num[i] = Integer.parseInt(input[i]);
// 前缀和下标从1开始
preSum[i + 1] = preSum[i] + num[i];
}
// 只看单独一个数都属于
int cnt = n;
for (int i = 0; i < n; i++) {
long mul = num[i];
for (int j = i + 1; j < n; j++) {
mul *= num[j];
// 求区间和
long cur = preSum[j + 1] - preSum[i];
if (mul == cur) {
cnt++;
continue;
}
// 只有乘积小于累加和时才有机会
if (mul > cur) break;
}
}
writer.write(cnt + "");
writer.flush();
}
}
🍩H 巧克力
有动态规划的味道,但是又不是那么强烈。考虑一种贪心算法,将所有巧克力按照价格从小到大排序,然后枚举每个巧克力,需要考虑巧克力的保质期和数量,同时要注意,这x天,只要每天有巧克力吃就行,也就是让这x天每天都有至少一块巧克力就行,为了避免某一天重复购买多块巧克力,可以使用have[] boolean数组来记录当前天是否有巧克力吃。
在遍历每块巧克力的同时,记录能够满足吃巧克力的天数,和其花费,最后比较这个天数是否为题目要求的x天,如果是,那就满足要求,直接输出花费;如果不是,输出-1。
import java.util.*;
import java.io.*;
public class Main {
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int x = Integer.parseInt(input[0]);
int n = Integer.parseInt(input[1]);
int[][] choices = new int[100001][3];
for (int i = 0; i < n; i++) {
// 分别记录单价、保质期、数量
input = reader.readLine().trim().split(" ");
choices[i][0] = Integer.parseInt(input[0]);
choices[i][1] = Integer.parseInt(input[1]);
choices[i][2] = Integer.parseInt(input[2]);
}
// 记录能够吃的天数
int day = 0;
// 记录最小花费
int money = 0;
// 贪心算法,将巧克力按单价从小到大排序
Arrays.sort(choices, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
// 记录x天中的每天是否有巧克力吃
boolean[] have = new boolean[x + 1];
// 遍历所有巧克力
// 站在全局考虑,只需要x天,每天都有巧克力吃就行
// 用have数组避免重复购买巧克力
for (int[] c : choices) {
// 当前巧克力相较于刚开始还有保质期
// 当前巧克力还有剩
while (c[1] > 0 && c[2] > 0) {
if (have[c[1]] == false) {
have[c[1]] = true;
// 巧克力数量-1
c[2]--;
// 满足的天数+1
day++;
// 记录最小代价
money += c[0];
}
// 如果当前日子已经有满足条件的巧克力那就不用管
c[1]--; // 无论条件是否满足,巧克力的保质期都要--
}
}
// 如果最终的天数满足题意,输出最小代价
System.out.println(day == x ? money : -1);
}
}
🥘I 翻转括号序列
看题解说是用线段树解?先放放,做不来。
🥠 J 异或三角
做不来!
总结一下,填空题还能做,大题前几道也还行,后面越做人越麻,不愧是国赛,用来拉开差距的,希望我能拿个国三,谢天谢地了!