惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
T
Tenable Blog
博客园_首页
S
Securelist
Spread Privacy
Spread Privacy
Google Online Security Blog
Google Online Security Blog
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
U
Unit 42
L
LINUX DO - 热门话题
量子位
T
Threat Research - Cisco Blogs
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
Martin Fowler
Martin Fowler
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Security Latest
Security Latest
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
InfoQ
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
V2EX - 技术
V2EX - 技术
T
Tailwind CSS Blog
月光博客
月光博客
Recent Announcements
Recent Announcements
G
Google Developers Blog
F
Full Disclosure
W
WeLiveSecurity
宝玉的分享
宝玉的分享
腾讯CDC
G
GRAHAM CLULEY
Vercel News
Vercel News
Simon Willison's Weblog
Simon Willison's Weblog
美团技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security

qlAD 的技术笔记

2026 年 6 月 7 日 -- 我有社交恐惧症,恐惧外面到处雷电风雨声(大学生活记录) 手把手带你实现哈希表(C/C++ 数据结构) 手把手带你实现图结构(C/C++ 数据结构) 手把手带你实现二叉树(C/C++ 数据结构) 手把手带你实现栈和队列(C/C++ 数据结构) 手把手带你实现单/双链表(C/C++ 数据结构) 手把手带你实现动态数组(C/C++ 数据结构) 2026 年 1 月 2 日 -- 我不怕冷,因为我内心炽热 【2025 年度总结】(大学生活记录) 2025 年 12 月 2 日 -- 省电模式:大一 100 天耗电日志(大学生活记录) 2025 年 11 月 3 日 -- 用爱发电、大爱无私(大学生活记录) 使用循环嵌套输出规则图形找规律讲解(嵌套循环-图形输出) 2025 年 10 月 1 日 -- 二字开头第一年的生日记录及感悟随笔(大学生活记录) Python 初级 04 -- 目前为止 Python 中你们做题可能会遇到的问题以及七七八八 2025 年 9 月 24 日 -- 开学后的第一课及军训汇演(大学生活记录) Python 初级 03 -- 程序设计的三种结构之选择结构和循环结构 Python 初级 02 -- 基础的数据类型和输入输出 2025 年 9 月 17 日 -- 大连 2025 国际大体联足球世界杯(大学生活记录) Python 初级 01 -- 初识编程、理解计算机语言及程序运行方式 适合大一新生学习编程的前一课 -- 编程先导(编程的本质) 2025 年 9 月 12 日 -- 三天军训感受以及一些七七八八(大学生活记录) 2025 年 9 月 9 日 -- 体检、军训前的准备(大学生活记录) 适合大一新生的计算机基础知识 -- 个人认为足够版 2025 年 9 月 7 日 -- 既来之、则安之(大学生活记录) C++ 程序的内存布局 —— 代码区、全局/静态区、栈区和堆区 联想小新 pro 16 2025 开箱记录:跳过联网、office 激活、更换 win 11 专业版 R 语言中的数学函数 计算机软件著作权申请过程记录 从聚类到回归:用 Python 解析鸢尾花数据集的完整数据科学流程 条件概率、全概率与贝叶斯公式 —— 一篇文章带你死磕概率公式 高中化学物质反应规则表 —— 方程式之间也有规律可循 集合元素性质、关系、子集公式、基本运算 —— 一篇文章带你全方面死磕集合 如何为你的项目选择合适的许可证 -- 全方位指南 解释 3:1 和 9:3:3:1 的详细来源 —— 建立减数分裂与孟德尔遗传学定律之间的联系 重新设置磁盘分区以及 Btrfs 子卷(subvolume)布局的无损方案 零基础实战:用 Docker 启动 dockermailserver 绕过 25 端口封锁搭建个人邮箱服务器 基于 GitHub Actions + OSS 的自托管 APT 仓库全面指南 如何在安装 Debian 时配置 Btrfs 子卷 | 详细分步指南 一个合格的个人网站站长应该知道哪些事? CSS 基础入门教程 —— 选择器、样式声明 HTML 基础入门 —— 基本结构和语法 给博客添加一个文章数据统计的页面 Git 学习笔记(命令备忘表) Anki 牌组选项详细解释与设置 手动编译带有 KernelSU 的 OnePlus 6 内核 安卓类原生系统补全计划 —— PixelExperience(一加 OnePlus) 如何使用 use-sound 为 React 应用添加声音效果 标准化项目的 GitHub Flow 工作流 如何搭建设计自己的博客网站? 使用安知鱼主题同款的 Twikoo 评论组件 配置 GitHub Actions 实现自动化提交百度收录 第00期 | 环境搭建 & 递归 (一) | 基本数列递归 逐字符讲解 C 语言 HelloWorld 程序 2023 年回顾 🤟 Anki:记忆神器,助你高效学习 Gentoo 学习笔记 美化你的 PixelExperience OS Markdown 语法指南 搭建 OneDrive 目录索引 为什么用 65 表示大写字母 A
蓝桥杯备赛第一期 —— 枚举与模拟
qlAD · 2025-11-15 · via qlAD 的技术笔记

cover

在算法竞赛中,枚举与模拟是最基础也最常用的解题思想。无论是入门级的题目还是复杂的综合题,都能看到它们的身影。蓝桥杯作为国内颇具影响力的编程赛事,对枚举与模拟的考察更是贯穿各个难度层级。今天我们就来系统梳理这两种思想的核心要点,并通过实例掌握其在实战中的应用。

一、枚举:暴力美学的艺术

枚举(Enumeration),又称穷举,核心思想是逐个列举所有可能的情况,并判断是否符合条件。虽然听起来"暴力",但在数据范围允许的情况下,枚举往往是最简单直接的解题方案。

枚举的适用场景:

  • 问题的解空间有限且规模较小
  • 无法找到更高效的数学规律或算法
  • 需要验证某个结论的正确性

经典例题:水仙花数

问题描述:打印出所有的"水仙花数"。水仙花数是指一个三位数,其各位数字立方和等于该数本身(例如:153 = 1³ + 5³ + 3³)。

C++实现:

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    for (int num = 100; num < 1000; num++) {
        int a = num / 100;         // 百位
        int b = num / 10 % 10;     // 十位
        int c = num % 10;          // 个位
        if (a*a*a + b*b*b + c*c*c == num) {
            cout << num << endl;
        }
    }
    return 0;
}

Java实现:

public class Main {
    public static void main(String[] args) {
        for (int num = 100; num < 1000; num++) {
            int a = num / 100;         // 百位
            int b = num / 10 % 10;     // 十位
            int c = num % 10;          // 个位
            if (a*a*a + b*b*b + c*c*c == num) {
                System.out.println(num);
            }
        }
    }
}

Python实现:

for num in range(100, 1000):
    a = num // 100          # 百位
    b = num // 10 % 10      # 十位
    c = num % 10            # 个位
    if a**3 + b**3 + c**3 == num:
        print(num)

二、模拟:让计算机复现过程

模拟(Simulation)是指按照题目描述的逻辑,一步步复现事件的发生过程。这类题目通常不涉及复杂算法,但需要精准理解题意,将文字描述转化为代码逻辑。

模拟的关键要点:

  • 理清题目中的流程和规则
  • 设计合适的数据结构存储中间状态
  • 处理边界条件和特殊情况

经典例题:模拟日期

问题描述:给定一个日期(年、月、日),模拟推进 n 天后的新日期。

C++实现:

#include <bits/stdc++.h>
using namespace std;

bool isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

int daysInMonth(int month, int year) {
    int days[] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (month == 2 && isLeapYear(year)) return 29;
    return days[month];
}

void nextDate(int &year, int &month, int &day, int n) {
    for (int i = 0; i < n; i++) {
        day++;
        if (day > daysInMonth(month, year)) {
            day = 1;
            month++;
            if (month > 12) {
                month = 1;
                year++;
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    int year, month, day, n;
    cin >> year >> month >> day >> n;
    nextDate(year, month, day, n);
    cout << year << " " << month << " " << day << endl;

    return 0;
}

Java实现:

import java.util.Scanner;

public class Main {
    static boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }

    static int daysInMonth(int month, int year) {
        int[] days = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        if (month == 2 && isLeapYear(year)) return 29;
        return days[month];
    }

    static void nextDate(int[] date, int n) {
        for (int i = 0; i < n; i++) {
            date[2]++;
            if (date[2] > daysInMonth(date[1], date[0])) {
                date[2] = 1;
                date[1]++;
                if (date[1] > 12) {
                    date[1] = 1;
                    date[0]++;
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int year = sc.nextInt();
        int month = sc.nextInt();
        int day = sc.nextInt();
        int n = sc.nextInt();
        nextDate(date, n);
        System.out.println(year + " " + month + " " + day);
    }
}

Python实现:

def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

def days_in_month(month, year):
    days = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if month == 2 and is_leap_year(year):
        return 29
    return days[month]

def next_date(year, month, day, n):
    for _ in range(n):
        day += 1
        if day > days_in_month(month, year):
            day = 1
            month += 1
            if month > 12:
                month = 1
                year += 1
    return year, month, day

year, month, day = map(int, input().split())
n = int(input())
year, month, day = next_date(year, month, day, n)
print(year, month, day)

三、实战技巧与注意事项

  1. 枚举的优化

    • 减少枚举维度(例如用数学关系排除无效解)
    • 缩小枚举范围(通过题目约束条件剪枝)
    • 避免重复计算(缓存中间结果)
  2. 模拟的细节处理

    • 仔细阅读题目,确保理解所有规则
    • 先用纸笔模拟小案例,验证逻辑正确性
    • 注意循环边界和索引计算(尤其是涉及取模的场景)
  3. 蓝桥杯常见坑点

    • 数据范围:枚举前务必确认是否会超时
    • 输入输出格式:严格按照题目要求处理(例如多组测试数据)
    • 整数溢出:C++/Java中需注意数据类型选择(如用long long)

四、总结

枚举与模拟是算法入门的基石,也是蓝桥杯初赛的重点考察内容。掌握这两种思想,不仅能解决直接考察它们的题目,更为学习复杂算法打下基础。

建议通过以下方式巩固练习:

  1. 完成蓝桥杯真题中标记为"枚举"或"模拟"的题目
  2. 尝试用不同语言实现同一道题,熟悉语法差异
  3. 对枚举题思考是否有优化空间,提升代码效率

五、蓝桥杯真题

1、卡片(2021 年省赛)

小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。

小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。

小蓝想知道自己能从 1 拼到多少。

例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。

现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1 拼到多少?

#include <bits/stdc++.h>
using namespace std;

int cnt[10];

bool check(int x) {
    while (x != 0) {
        int digit = x % 10;
        if (cnt[digit] == 0) return false;
        cnt[digit]--;
        x /= 10;
    }
    return true;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    for (int i = 0; i < 10; i++) {
        cnt[i] = 2021;    // 0 ~ 9 各 2021 张
    }

    for (int i = 1; ; i++) {
        if (!check(i)) {
            cout << i - 1 << endl;
            break;
        }
    }

    return 0;
}
import java.util.Scanner;

public class Main {
    static int[] cnt = new int[10];

    public static boolean check(int num) {
        while (num != 0) {
            int digit = num % 10;
            if (cnt[digit] == 0) return false;
            cnt[digit]--;
            num /= 10;
        }
        return true;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            cnt[i] = 2021;    // 0 ~ 9 各 2021 张
        }

        for (int i = 1; ; i++) {
            if (!check(i)) {
                System.out.println(i - 1);
                break;
            }
        }
    }
}
cnt = [2021] * 10  # 0 ~ 9 各 2021 张
def check(x):
    while x != 0:
        digit = x % 10
        if cnt[digit] == 0:
            return False
        cnt[digit] -= 1
        x //= 10
    return True

for i in range(1, 10**9):
    if not check(i):
        print(i - 1)
        break

2、回文日期(2020 年省赛)

2020年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为如果将这个日期按“yyyymmdd”的格式写成一个 8位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。

有人表示 20200202 是“千年一遇”的特殊日子。对此小明很不认同,因为不到2年之后就是下今个回文日期:20211202即2021年12月2日。

也有人表示 20200202并不仅仅是一个回文日期,还是一个 ABABBABA型的回文日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA 型的回文日期:21211212即212年12月12日。算不上“千年一遇”,顶多算“千年两遇”。

给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA 型的回文日期各是哪一夫。

#include <bits/stdc++.h>
using namespace std;

int date, month[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};

string check(int date) {
    string s = to_string(date), t = to_string(date);
    reverse(t.begin(), t.end());
    s+=t;
    int y = stoi(s.substr(0,4)), m = stoi(s.substr(4,2)), d = stoi(s.substr(6,2));
    if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) month[2] = 29;
    else month[2] = 28;
    if (m < 1 || m > 12 || d < 1 || d > month[m]) return "-1";
    return s;
}

string check2(int date) {
    string s = to_string(date), t = to_string(date);
    reverse(t.begin(), t.end());
    s+=t;
    if (s[0] == s[2] && s[0] == s[5] && s[0] == s[7] && s[1] == s[3] && s[1] == s[4] && s[1] == s[6]) return s;
    return "-1";
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> date;
    string ans1 = "";
    for (int i = date / 10000; ; i++) {
        string res = check(i);
        if ((check(i) == "-1") || check(i) == to_string(date)) continue;
        if (ans1 == "") ans1 = check(i);
        if (check2(i) != "-1") {
            cout << ans1 << endl << check2(i) << endl;
            return 0;
        }
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    static int[] month = {-1,31,28,31,30,31,30,31,31,30,31,30,31};

    public static String check(int date) {
        String s = Integer.toString(date);
        String t = new StringBuilder(s).reverse().toString();
        s += t;
        int y = Integer.parseInt(s.substring(0,4));
        int m = Integer.parseInt(s.substring(4,6));
        int d = Integer.parseInt(s.substring(6,8));
        if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) month[2] = 29;
        else month[2] = 28;
        if (m < 1 || m > 12 || d < 1 || d > month[m]) return "-1";
        return s;
    }

    public static String check2(int date) {
        String s = Integer.toString(date);
        String t = new StringBuilder(s).reverse().toString();
        s += t;
        if (s.charAt(0) == s.charAt(2) && s.charAt(0) == s.charAt(5) && s.charAt(0) == s.charAt(7) &&
            s.charAt(1) == s.charAt(3) && s.charAt(1) == s.charAt(4) && s.charAt(1) == s.charAt(6)) {
            return s;
        }
        return "-1";
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int date = sc.nextInt();
        String ans1 = "";
        for (int i = date / 10000; ; i++) {
            String res = check(i);
            if (res.equals("-1") || res.equals(Integer.toString(date))) continue;
            if (ans1.equals("")) ans1 = res;
            if (!check2(i).equals("-1")) {
                System.out.println(ans1);
                System.out.println(check2(i));
                return;
            }
        }
    }
}
month = [-1,31,28,31,30,31,30,31,31,30,31,30,31]
def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
def check(date):
    s = str(date)
    t = s[::-1]
    s += t
    y = int(s[0:4])
    m = int(s[4:6])
    d = int(s[6:8])
    if is_leap_year(y):
        month[2] = 29
    else:
        month[2] = 28
    if m < 1 or m > 12 or d < 1 or d > month[m]:
        return "-1"
    return s
def check2(date):
    s = str(date)
    t = s[::-1]
    s += t
    if s[0] == s[2] and s[0] == s[5] and s[0] == s[7] and s[1] == s[3] and s[1] == s[4] and s[1] == s[6]:
        return s
    return "-1"
date = int(input())
ans1 = ""
for i in range(date // 10000, 10**9):
    res = check(i)
    if res == "-1" or res == str(date):
        continue
    if ans1 == "":
        ans1 = res
    if check2(i) != "-1":
        print(ans1)
        print(check2(i))
        break