2021年4月18日那场蓝桥杯省赛,Java 大学B组第一场的十道题,到现在仍然是很多人刷题清单里的必做卷。这套省赛真题值得反复过,是因为它把填空题的计算量、数论里的约数枚举、图论的建图与最短路、动态规划的状态设计,还有大规模数据下的算法优化,全都均匀地铺在了十道题里。你要是能不看题解独立把这一场从头做到尾,省赛B组常见的套路基本就过了一遍。
下面我按题号 A 到 J 一道道拆,包含我自己的思路、能直接跑的 Java 代码、参数上的取舍理由,以及当年现场踩过、后来复盘才想明白的坑。刚学完 Java 基础语法想找套真题练手的同学,可以只挑前六题;已经刷过几十道题的,可以重点看后面四道大题的优化。所有代码都是标准输入输出,直接丢进评测机就能跑。
1. 先把卷面摸清楚:150分怎么分布
蓝桥杯 Java 大学B组省赛的满分是 150 分,十道题按难度递增给分。第一场这套卷子的分值分布是:A 到 E 是填空题(结果填空,不用提交代码),每题 5 到 15 分;F 到 J 是编程题,每题 15 到 25 分。这个结构意味着一个很现实的策略:填空题哪怕敲代码暴力算,也基本能拿满,编程题则要按测试点分档拿分。
1.1 十道题的题型地图
先把题号和考点列出来,这张表建议存下来,复习的时候对着看。
| 题号 | 题目 | 分值 | 核心考点 | 参考答案 |
|---|---|---|---|---|
| A | ASC | 5 | 字符编码 | 76 |
| B | 卡片 | 5 | 模拟 / 数位统计 | 3181 |
| C | 直线 | 10 | 枚举 + 去重(斜率+截距) | 40257 |
| D | 货物摆放 | 10 | 约数枚举 + 三重循环 | 2430 |
| E | 路径 | 15 | 最小公倍数建图 + 最短路 | 10266837 |
| F | 时间显示 | 15 | 取模 + 格式化输出 | 按输入输出 |
| G | 砝码称重 | 20 | 三态背包 DP | 按输入输出 |
| H | 杨辉三角形 | 20 | 组合数 + 二分 | 按输入输出 |
| I | 双向排序 | 25 | 单调栈 + 两端填充 | 按输入输出 |
| J | 括号序列 | 25 | 计数 DP + 拆分子问题 | 按输入输出 |
前十题里,A、B、F 属于「送分但不能错」,C、D、E、G、H 属于「有明确套路的中档」,I、J 是拉开差距的两道。这个梯度很典型,刷题的时候可以按这个分组去练。
1.2 为什么第一场的题比第二场更值得先刷
同一个知识点,不同场次的考法会不一样。第十二届 Java B 组第一场的特点在于:它把「优化」这件事的入口放得很明显,但门槛藏得很隐蔽。比如 I 题双向排序,n 和 m 都是 10 万级别,你要是老老实实每次排序都调Arrays.sort,复杂度直接 O(m·n log n),能过的测试点非常少;但题面又给了一个强提示——初始序列是 1 到 n 的升序排列。这个「初始有序」的条件就是整道题的钥匙。
再比如 J 题括号序列,看起来是个「最少添加括号 + 计数」的组合问题,其实官方解法把它拆成了两个完全独立、可以分别用线性 DP 求解的子问题,再把两个结果乘起来。这种「看起来要一起做、其实可以拆开做」的思路,在省赛里反复出现,练一遍就能迁移到别的题上。
1.3 时间分配上的一个实用建议
省赛是 4 小时,十道题。我自己的节奏是:前 60 分钟必须把 A 到 F 全部拿下并检查一遍,剩下的 3 小时全砸在 G 到 J 上。原因很直接——前面六题的总分是 65 分,占了一半不到,但拿满的确定性接近 100%;后面四题一共 85 分,每道题都可能有「暴力能过一部分、正解才能满分」的分档。先把确定的分揣兜里,再去啃不确定的,心理压力会小很多,也不会出现「难题没做出来、简单题手滑算错」的惨案。
还有一个细节:填空题不许提交代码,只填答案,所以你算出来的数字一定要用第二种方法复核。我见过太多人在 B 题卡片上写完循环就交了,结果循环边界写成了<=还是<自己都没想清楚。
2. 填空题:手算 + 代码双验证
填空题在整个卷子里性价比最高,因为可以用任意手段求解——手推、写程序暴力、甚至查资料都行,只看最后那个数字对不对。但正因为没有过程分,翻车也翻得最彻底。
2.1 ASC:送分题里藏着的字母序坑
这题问的是:已知大写字母 A 的 ASCII 码是 65,那么大写字母 L 的 ASCII 码是多少。
答案 76。推理很简单:A 是第 1 个字母对应 65,B 是 66,以此类推,L 是第 12 个字母,65 + (12 - 1) = 76。
但踩坑点在这里:很多人会心算成 65 + 12 = 77,忘了索引从 0 偏移。写代码验证只要一行:
public class Main { public static void main(String[] args) { System.out.println((int) 'L'); // 输出 76 } }提示:凡是「第 n 个字母对应多少」这类问题,先在纸上写 A=65、B=66、C=67 三个真实值对齐一次,确认偏移量是 n-1 而不是 n,再动手算。
这题真正的价值不在它本身,而在于提醒你:第一题最容易因为轻敌丢分。省赛开考后的前五分钟,人还没进入状态,看到这种题容易直接凭感觉填。
2.2 卡片:从 1 数到 3181,数字 1 到底用了几张
题目大意是:手里有 0 到 9 的卡片各 2021 张,从 1 开始一个一个往下拼数(1、2、3……),每拼一个数卡片就被消耗,问最多能拼到多少。答案是 3181。
这题本质是问:从 1 到 n,数字 '1' 一共出现了多少次,第一次超过 2021 时 n 是多少。为什么盯住 '1'?因为从 1 开始连续数,'1' 出现的频率通常最高,瓶颈一定出现在它身上。
最稳的做法是直接模拟:
public class Main { public static void main(String[] args) { int[] cnt = new int[10]; for (int i = 0; i < 10; i++) cnt[i] = 2021; int n = 0; outer: while (true) { n++; int t = n; while (t > 0) { int d = t % 10; if (--cnt[d] < 0) { System.out.println(n - 1); // 这个数拼不出来 break outer; } t /= 10;