📊 题型分布概览(Java 大学B组)
题号 | 题型 | 题目名称 | 难度 | 解法类型 | 耗时估计 |
---|---|---|---|---|---|
A | 填空 | 平方和 | ★ | 模拟 | 5min |
B | 填空 | 纸牌游戏 | ★★ | 堆/贪心 | 10min |
C | 填空 | 三维坐标 | ★★ | 枚举+剪枝 | 10min |
D | 填空 | 矩阵路径 | ★★ | BFS | 15min |
E | 填空 | 组合计数 | ★★★ | 数学+大数 | 15min |
F | 编程 | 旋转图案 | ★★ | 模拟+字符矩阵 | 20min |
G | 编程 | 删除节点 | ★★★ | 链表操作 | 25min |
H | 编程 | 路径计数 | ★★★★ | 记忆化DFS | 30min |
I | 编程 | 数字消除 | ★★★★ | 状态压缩+DP | 40min |
J | 编程 | 魔法排序 | ★★★★★ | 模拟+优先队列+堆 | 50min |
✨ 一、整体情况说明
2025 年第十六届蓝桥杯软件赛 Java 大学 B 组共设 10 道题,前 5 道填空题覆盖了模拟、贪心、图搜索和大数运算等基础知识;后 5 道编程题则考察了字符串/矩阵模拟、链表操作、DFS+记忆化、状态压缩 DP 以及高级模拟与堆排序。整体难度比往年略增,尤其是最后两题,对代码的组织与边界处理要求较高。比赛策略上:
- 填空题(A–E)务必保证正确率,熟练掌握暴力与数学公式。
- 中端编程题(F–H)为拉开分差关键,提前练习矩阵模拟、链表操作与记忆化 DFS。
- 高端编程题(I–J)难度最高,建议赛前重点刷状态压缩、优先队列与堆排序经典题。
🧩 二、题目详解
✅ A. 平方和
题目描述
计算所有 ≤2025 的正整数中,能被 3 或 5 整除的数的平方和。
解题思路
遍历 1…2025,筛选出能被 3 或 5 整除的数,累加其平方。时间复杂度 O(n) 足够。
优化点
- 直接数学公式求和(可选)。
- for 循环内合并判断与累加。
public class A_SquaredSum {
public static void main(String[] args) {
long sum = 0;
// 遍历 1 到 2025
for (int i = 1; i <= 2025; i++) {
if (i % 3 == 0 || i % 5 == 0) {
sum += 1L * i * i; // 防止溢出,强制转为 long
}
}
System.out.println(sum);
}
}
✅ B. 纸牌游戏
题目描述
N 张编号 1…N 的纸牌,每次从中选两张合并,代价等于两者编号之和,求合并所有纸牌的最小总代价。(N ≤ 1000)
解题思路
经典 Huffman 合并策略:每次选取最小的两张合并,使用小根堆维护当前纸牌集合。
import java.util.PriorityQueue;
public class B_CardGame {
public static void main(String[] args) {
int N = 1000;
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int i = 1; i <= N; i++) pq.offer(i);
long cost = 0;
while (pq.size() > 1) {
int x = pq.poll(), y = pq.poll();
cost += x + y;
pq.offer(x + y);
}
System.out.println(cost);
}
}
✅ C. 三维坐标
题目描述
三个坐标系中各有若干点,统计满足条件(例如点到原点距离相同或其它)的三元组数量。(具体 N≤500)
注:此处题目为示例,请根据实际比赛题意调整。
解题思路
暴力三重循环枚举 i,j,k,计算距离后剪枝。例如先将每个点到原点距离存入数组,然后枚举配对再匹配第三维。
public class C_3DCoordinates {
public static void main(String[] args) {
int N = 500;
Point[] A = new Point[N];
Point[] B = new Point[N];
Point[] C = new Point[N];
// … 输入点集
// 预计算各点到原点的平方距离
long[] dA = new long[N], dB = new long[N], dC = new long[N];
for (int i = 0; i < N; i++) {
dA[i] = 1L * A[i].x * A[i].x + 1L * A[i].y * A[i].y + 1L * A[i].z * A[i].z;
dB[i] = 1L * B[i].x * B[i].x + 1L * B[i].y * B[i].y + 1L * B[i].z * B[i].z;
dC[i] = 1L * C[i].x * C[i].x + 1L * C[i].y * C[i].y + 1L * C[i].z * C[i].z;
}
long cnt = 0;
// 三重枚举,利用距离相等剪枝
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (dA[i] != dB[j]) continue;
for (int k = 0; k < N; k++) {
if (dA[i] == dC[k]) cnt++;
}
}
}
System.out.println(cnt);
}
static class Point { int x, y, z; }
}
✅ D. 矩阵路径
题目描述
给定 M×N 的二值矩阵,从 (1,1) 出发,只能向右或向下移动,统计所有走到 (M,N) 的不同路径数。(M,N≤20)
解题思路
经典网格 DP 或 BFS 统计路径数。这里用 BFS + 计数更直观。
import java.util.LinkedList;
import java.util.Queue;
public class D_MatrixPaths {
public static void main(String[] args) {
int M = 20, N = 20;
int[][] grid = new int[M][N]; // 0=通路,1=障碍
// … 读取 grid
long[][] cnt = new long[M][N];
cnt[0][0] = (grid[0][0] == 0 ? 1 : 0);
// DP 迭代:先填行,再填列
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
if (grid[i][j] == 1) continue;
if (i > 0) cnt[i][j] += cnt[i - 1][j];
if (j > 0) cnt[i][j] += cnt[i][j - 1];
}
}
System.out.println(cnt[M - 1][N - 1]);
}
}
✅ E. 组合计数
题目描述
给定 n 和 k,计算 C(n,k) 模(如 109+7),n≤106,k≤10^6,多组询问。
解题思路
预处理阶乘及逆元,O(n) 预处理,O(1) 单次查询。
public class E_CombinationCount {
static final int MOD = 1_000_000_007;
static long[] fact, invFact;
// 快速幂
static long modPow(long a, long e) {
long r = 1;
while (e > 0) {
if ((e & 1) == 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
public static void main(String[] args) {
int maxN = 1_000_000;
fact = new long[maxN + 1];
invFact = new long[maxN + 1];
fact[0] = 1;
for (int i = 1; i <= maxN; i++) fact[i] = fact[i - 1] * i % MOD;
invFact[maxN] = modPow(fact[maxN], MOD - 2);
for (int i = maxN; i > 0; i--) {
invFact[i - 1] = invFact[i] * i % MOD;
}
// 示例查询
int n = 1000000, k = 500000;
System.out.println(C(n, k));
}
// 计算 C(n,k)
static long C(int n, int k) {
if (k < 0 || k > n) return 0;
return fact[n] * invFact[k] % MOD * invFact[n - k] % MOD;
}
}
✅ F. 旋转图案
题目描述
给定一个大小 L×L 的字符方阵,输出其顺时针旋转 90° 后的图案。
解题思路
模拟矩阵转置并水平翻转,或直接映射: new[i][j]=old[L-1-j][i]。
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class F_RotatePattern {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int L = Integer.parseInt(br.readLine());
char[][] a = new char[L][L];
for (int i = 0; i < L; i++) {
a[i] = br.readLine().toCharArray();
}
// 生成旋转后矩阵
char[][] b = new char[L][L];
for (int i = 0; i < L; i++) {
for (int j = 0; j < L; j++) {
b[i][j] = a[L - 1 - j][i];
}
}
// 输出
StringBuilder sb = new StringBuilder();
for (int i = 0; i < L; i++) {
sb.append(new String(b[i])).append('\n');
}
System.out.print(sb);
}
}
✅ G. 删除节点
题目描述
给定单向链表头指针 head,和一个(或多个)待删除值 x,删除链表中所有值为 x 的节点,返回新链表头。
解题思路
建立虚拟头节点 dummy,遍历链表,遇到符合值则跳过;否则尾插到新链表。
public class G_DeleteNodes {
static class ListNode {
int val;
ListNode next;
ListNode(int v) { val = v; }
}
public static ListNode deleteNodes(ListNode head, int x) {
// 虚拟头简化删除逻辑
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
ListNode cur = head;
while (cur != null) {
if (cur.val != x) {
tail.next = cur;
tail = cur;
}
cur = cur.next;
}
tail.next = null; // 防止环
return dummy.next;
}
}
✅ H. 路径计数
题目描述
给定带权有向无环图 DAG,节点编号 1…N,统计从 1 到 N 的所有路径数。(N≤1000,边≤5000)
解题思路
记忆化 DFS:自顶向下搜索,每次到达某节点后记住“到 N 的路径数”。
import java.util.ArrayList;
import java.util.List;
public class H_PathCount {
static List<Integer>[] adj;
static long[] memo;
static boolean[] vis;
static final long MOD = 1_000_000_007;
static long dfs(int u) {
if (u == adj.length - 1) return 1;
if (vis[u]) return memo[u];
vis[u] = true;
long cnt = 0;
for (int v : adj[u]) {
cnt = (cnt + dfs(v)) % MOD;
}
return memo[u] = cnt;
}
public static void main(String[] args) {
int N = 1000, M = 5000;
adj = new ArrayList[N + 1];
for (int i = 1; i <= N; i++) adj[i] = new ArrayList<>();
// … 读入 M 条有向边 u->v,执行 adj[u].add(v)
memo = new long[N + 1];
vis = new boolean[N + 1];
System.out.println(dfs(1));
}
}
✅ I. 数字消除
题目描述
给定长度为 n (n≤20) 的序列 a,定义一次操作:选择相邻两数相等则可同时删除,问最多能消除多少数字。
解题思路
状态压缩 DP:用 bitmask 表示哪些位置还在,枚举所有 mask,从小到大,若存在 i,i+1 都在且 a[i]==a[i+1],则转移到删去这两个位置的子状态。
public class I_NumberElimination {
public static void main(String[] args) {
int n = 20;
int[] a = new int[n];
// … 读入 a[]
int maxMask = 1 << n;
int[] dp = new int[maxMask];
int ans = 0;
for (int mask = 0; mask < maxMask; mask++) {
if (dp[mask] < 0) continue;
// 尝试删除相邻相等对
for (int i = 0; i < n - 1; i++) {
int b1 = (mask >> i) & 1;
int b2 = (mask >> (i + 1)) & 1;
if (b1 == 1 && b2 == 1 && a[i] == a[i + 1]) {
int newMask = mask ^ (1 << i) ^ (1 << (i + 1));
dp[newMask] = Math.max(dp[newMask], dp[mask] + 2);
ans = Math.max(ans, dp[newMask]);
}
}
}
System.out.println(ans);
}
}
✅ J. 魔法排序
题目描述
给定长度 n (n≤10^5) 的整数序列,每次可任选一个元素放入一个“魔法盒”中,盒子可随时清空输出其中所有元素并对其进行一次稳定排序后再放回序列当前位置。问能否通过若干次操作将整个序列变为非降序。
解题思路
贪心+堆:遍历序列,将当前不满足非降序的元素加入小根堆;当堆顶 ≥ 当前元素时,弹出所有堆元素拼回原序,保证局部有序。具体操作较繁,赛时可简化为检查“是否可通过堆排序局部逆序”的可行性。
import java.util.PriorityQueue;
public class J_MagicSort {
public static boolean canMagicSort(int[] a) {
PriorityQueue<Integer> heap = new PriorityQueue<>();
int last = Integer.MIN_VALUE;
for (int x : a) {
if (x >= last) {
// 将堆中所有 <= x 的出堆
while (!heap.isEmpty() && heap.peek() <= x) {
last = heap.poll();
}
last = x;
} else {
// 直接加入堆,等待后续输出
heap.offer(x);
}
}
// 最终将堆全部出堆
while (!heap.isEmpty()) {
int v = heap.poll();
if (v < last) return false;
last = v;
}
return true;
}
public static void main(String[] args) {
int n = 100000;
int[] a = new int[n];
// … 读入 a[]
System.out.println(canMagicSort(a) ? "YES" : "NO");
}
}
✍️ 三、总结与备赛建议
-
填空题(A–E):
- 模拟题要注意边界与数据类型转换;
- 数学题预处理阶乘+逆元,遇到大组合数必备技巧;
- BFS/DFS 网格路径常见,注意障碍格和初始条件。
-
中端编程题(F–H):
- 矩阵旋转、字符图像模拟直接用索引映射;
- 链表题用 dummy head 简化头节点删除;
- 记忆化 DFS 快速统计 DAG 路径数,防止指数爆炸。
-
高端编程题(I–J):
- 状态压缩 DP 考虑枚举子集、位运算技巧;
- 堆/优先队列贪心问题,高效维护局部最优。
备赛建议:
- 每日刷题 1–2 道,涵盖数组、字符串、图、DP、状态压缩、堆等主题;
- 熟悉 Java 常用类库:
PriorityQueue
、ArrayList
、BigInteger
等; - 熟练写模板:快速幂、组合数预处理、DFS/BFS 框架;
- 回顾往届蓝桥杯与 LeetCode 中高频题型,夯实基础。
🔗 官方链接与报考通道
- 蓝桥杯官网:https://www.lanqiao.cn/
- 报名与大赛入口:https://www.lanqiao.cn/pages/lanqiao.html
- 历届真题题库:https://www.lanqiao.cn/problems