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

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

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

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@u@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值