【2021年蓝桥杯Java-B组国赛题解】

博客内容涉及2021年蓝桥杯国赛Java-B组的多项算法题目,包括整数范围的最大值计算、线性筛模板求纯质数、时间模拟计算完全日期、记忆化搜索与动态规划解决最小权值问题、字符串大写、模拟求解序列和乘积、动态规划解决巧克力分配问题以及翻转括号序列和异或三角的算法探讨。
摘要由CSDN通过智能技术生成


国赛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 异或三角

在这里插入图片描述
做不来!

总结一下,填空题还能做,大题前几道也还行,后面越做人越麻,不愧是国赛,用来拉开差距的,希望我能拿个国三,谢天谢地了!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值