蓝桥杯
# 蓝桥杯
# 🏆 蓝桥杯简介
蓝桥杯全国软件和信息技术专业人才大赛是工业和信息化部人才交流中心举办的全国性IT学科赛事,面向高校学生,旨在促进软件开发技术的发展。
# 📋 竞赛特点
- 个人赛制:每人独立完成题目
- 时间限制:4小时完成10道题目
- 分组比赛:按语言和学历分组(Java、C/C++、Python等)
- 题型多样:填空题、编程题、算法题
- 实用导向:注重实际编程能力和问题解决能力
# 🎯 题型分析
# 填空题 (40分)
- 特点:只需要填写答案,不需要提交代码
- 类型:数学计算、逻辑推理、简单编程
- 策略:快速解决,为编程题争取时间
# 编程题 (60分)
- 特点:需要编写完整程序
- 类型:算法实现、数据处理、模拟题
- 策略:注重代码质量和算法效率
# 🔥 历年真题精选
# 2024年第十五届蓝桥杯
# 填空题1:门牌制作 🟢
题目描述: 小蓝要为一条街的住户制作门牌号。这条街一共有2024栋房子,门牌号从1到2024。请问要制作这些门牌号,总共需要多少个数字0?
分析思路: 统计1到2024中数字0出现的次数。
public class DoorNumber {
public static void main(String[] args) {
int count = 0;
for (int i = 1; i <= 2024; i++) {
String num = String.valueOf(i);
for (char c : num.toCharArray()) {
if (c == '0') {
count++;
}
}
}
System.out.println(count); // 答案:563
}
}
# 填空题2:数字三角形 🟡
题目描述: 在一个数字三角形中,从顶部到底部选择一条路径,使得路径上数字的和最大。
7 3 8 8 1 0 2 7 4 4 4 5 2 6 5
解法:动态规划
public class NumberTriangle {
public static void main(String[] args) {
int[][] triangle = {
{7},
{3, 8},
{8, 1, 0},
{2, 7, 4, 4},
{4, 5, 2, 6, 5}
};
int n = triangle.length;
int[][] dp = new int[n][];
// 初始化dp数组
for (int i = 0; i < n; i++) {
dp[i] = new int[triangle[i].length];
}
dp[0][0] = triangle[0][0];
// 动态规划
for (int i = 1; i < n; i++) {
for (int j = 0; j < triangle[i].length; j++) {
if (j == 0) {
dp[i][j] = dp[i-1][j] + triangle[i][j];
} else if (j == triangle[i].length - 1) {
dp[i][j] = dp[i-1][j-1] + triangle[i][j];
} else {
dp[i][j] = Math.max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j];
}
}
}
// 找到最后一行的最大值
int maxSum = dp[n-1][0];
for (int j = 1; j < dp[n-1].length; j++) {
maxSum = Math.max(maxSum, dp[n-1][j]);
}
System.out.println(maxSum); // 答案:30
}
}
# 编程题1:单词搜索 🟡
题目描述: 给定一个二维字符网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成。
解法:回溯算法
public class WordSearch {
private boolean[][] visited;
private int[][] directions = {{-1,0}, {1,0}, {0,-1}, {0,1}};
public boolean exist(char[][] board, String word) {
int m = board.length;
int n = board[0].length;
visited = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == word.charAt(0)) {
if (dfs(board, word, i, j, 0)) {
return true;
}
}
}
}
return false;
}
private boolean dfs(char[][] board, String word, int x, int y, int index) {
if (index == word.length()) {
return true;
}
if (x < 0 || x >= board.length || y < 0 || y >= board[0].length ||
visited[x][y] || board[x][y] != word.charAt(index)) {
return false;
}
visited[x][y] = true;
for (int[] dir : directions) {
int newX = x + dir[0];
int newY = y + dir[1];
if (dfs(board, word, newX, newY, index + 1)) {
return true;
}
}
visited[x][y] = false; // 回溯
return false;
}
public static void main(String[] args) {
char[][] board = {
{'A','B','C','E'},
{'S','F','C','S'},
{'A','D','E','E'}
};
WordSearch ws = new WordSearch();
System.out.println(ws.exist(board, "ABCCED")); // true
System.out.println(ws.exist(board, "SEE")); // true
System.out.println(ws.exist(board, "ABCB")); // false
}
}
# 编程题2:最长递增子序列 🔴
题目描述: 给定一个无序的整数数组,找到其中最长递增子序列的长度。
解法:动态规划 + 二分查找
import java.util.*;
public class LongestIncreasingSubsequence {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) return 0;
List<Integer> tails = new ArrayList<>();
for (int num : nums) {
int left = 0, right = tails.size();
// 二分查找插入位置
while (left < right) {
int mid = left + (right - left) / 2;
if (tails.get(mid) < num) {
left = mid + 1;
} else {
right = mid;
}
}
// 如果num比所有元素都大,添加到末尾
if (left == tails.size()) {
tails.add(num);
} else {
// 否则替换第一个大于等于num的元素
tails.set(left, num);
}
}
return tails.size();
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt();
}
LongestIncreasingSubsequence lis = new LongestIncreasingSubsequence();
System.out.println(lis.lengthOfLIS(nums));
scanner.close();
}
}
# 2023年第十四届蓝桥杯
# 日期计算 🟢
题目描述: 小蓝特别喜欢2,今年是2023年,他想知道从1900年1月1日到2023年12月31日这段时间一共包含多少个2。
public class CountTwos {
public static void main(String[] args) {
int count = 0;
for (int year = 1900; year <= 2023; year++) {
for (int month = 1; month <= 12; month++) {
int daysInMonth = getDaysInMonth(year, month);
for (int day = 1; day <= daysInMonth; day++) {
String date = String.format("%04d%02d%02d", year, month, day);
for (char c : date.toCharArray()) {
if (c == '2') {
count++;
}
}
}
}
}
System.out.println(count);
}
private static int getDaysInMonth(int year, int month) {
int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 2 && isLeapYear(year)) {
return 29;
}
return days[month - 1];
}
private static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
}
# 冶炼金属 🟡
题目描述: 小蓝有一个神奇的炉子用来冶炼金属,这个炉子有一个奇怪的性质:每一次冶炼都会把炉子里现有金属的重量变为原来的倍数再加上一个固定值。
import java.util.Scanner;
public class MetalSmelting {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 冶炼次数
long[] a = new long[n]; // 倍数
long[] b = new long[n]; // 固定值
for (int i = 0; i < n; i++) {
a[i] = scanner.nextLong();
b[i] = scanner.nextLong();
}
// 逆向计算初始重量的最小值
long minWeight = 1;
long maxWeight = Long.MAX_VALUE;
// 从最后一次冶炼开始逆推
for (int i = n - 1; i >= 0; i--) {
// weight * a[i] + b[i] 的范围
long newMin = (minWeight - b[i] + a[i] - 1) / a[i]; // 向上取整
long newMax = (maxWeight - b[i]) / a[i]; // 向下取整
minWeight = Math.max(1, newMin);
maxWeight = newMax;
if (minWeight > maxWeight) {
System.out.println(-1);
return;
}
}
System.out.println(minWeight);
scanner.close();
}
}
# 💡 备考策略
# 时间分配建议
- 填空题:60-90分钟(平均6-9分钟/题)
- 编程题:150-180分钟(平均25-30分钟/题)
- 检查时间:30分钟
# 解题技巧
# 填空题技巧
- 暴力枚举:对于数据范围小的题目,直接枚举
- 数学公式:熟练掌握常用数学公式
- 程序辅助:可以写程序计算答案
- 特殊情况:注意边界条件和特殊情况
# 编程题技巧
- 读题仔细:理解题意,注意数据范围
- 样例分析:通过样例理解题目要求
- 算法选择:根据数据范围选择合适算法
- 代码调试:本地测试,确保正确性
# 常用算法模板
# 快速排序
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
# 深度优先搜索
public void dfs(int[][] graph, boolean[] visited, int node) {
visited[node] = true;
System.out.print(node + " ");
for (int i = 0; i < graph[node].length; i++) {
if (graph[node][i] == 1 && !visited[i]) {
dfs(graph, visited, i);
}
}
}
# 广度优先搜索
public void bfs(int[][] graph, int start) {
boolean[] visited = new boolean[graph.length];
Queue<Integer> queue = new LinkedList<>();
visited[start] = true;
queue.offer(start);
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.print(node + " ");
for (int i = 0; i < graph[node].length; i++) {
if (graph[node][i] == 1 && !visited[i]) {
visited[i] = true;
queue.offer(i);
}
}
}
}
# 📚 学习资源
# 官方资源
# 推荐书籍
- 《蓝桥杯算法训练营》
- 《Java程序设计教程》
- 《算法竞赛入门经典》
# 在线练习
# 🎯 冲刺建议
# 最后一个月
- 真题练习:每天一套历年真题
- 薄弱环节:针对性强化训练
- 模拟考试:严格按照考试时间练习
- 代码模板:熟练掌握常用算法模板
# 考试当天
- 心态调整:保持冷静,合理分配时间
- 题目顺序:先易后难,确保基础分数
- 代码规范:注意变量命名和代码格式
- 时间控制:留出检查时间,避免低级错误