算法学习日记 | 模拟

🧠 算法学习日记 | 今天我用「模拟」解了三道题,原来"暴力"也能很聪明!

大家好,我是你们的算法学习搭子 👋

今天继续我的算法入门之旅,重点练习了**模拟(Simulation)**这一看似简单却极其实用的方法。

很多人觉得"模拟=暴力",但其实,模拟是把现实过程一步步还原到代码中。它不需要复杂的数学推导,只需要我们能清晰地理解问题逻辑,然后用循环和条件语句"一步一步走"。

今天我完整做了三道题,每一道都坚持用最朴素的模拟方法解决。下面我把题目原文我的原始代码原封不动贴出来,不做任何删减或美化,只为真实记录学习过程。


🔹 题目一:扫雷

题目描述

在一个 n m 列的方格图上有一些位置有地雷,另外一些位置为空。

请为每个空位置标一个整数,表示周围八个相邻的方格中有多少个地雷。
输入描述

输入的第一行包含两个整数 n, m

第 2 行到第 n+1 行每行包含 m 个整数,相邻整数之间用一个空格分隔。如果对应的整数为 0,表示这一格没有地雷。如果对应的整数为 1,表示这一格有地雷。

其中, 1 \\leq n, m \\leq 100
输出描述

输出 n 行,每行 m 个整数,相邻整数之间用空格分隔。

对于没有地雷的方格,输出这格周围的地雷数量。对于有地雷的方格,输出 9。
输入输出样例

复制代码
输入
3 4
0 1 0 0
1 0 1 0
0 0 1 0

输出
2 9 2 1
9 4 9 2
1 3 9 2

运行限制

  • 最大运行时间:1s
  • 最大运行内存:128M

✅ 我的代码

cpp 复制代码
#include <iostream>
using namespace std;

const int N=150;
int mp[N][N];
int ans[N][N];

int main()
{
    int m,n;
    cin>>m>>n;
    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j){
            cin>>mp[i][j];
        }
    }

    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j){
            if(mp[i][j]){
                ans[i][j]=9;
                continue;
            }

            for(int _i=max(1,i-1);_i<=min(m,i+1);++_i){
                for(int _j=max(1,j-1);_j<=min(n,j+1);++_j){
                    if(mp[_i][_j]){
                        ans[i][j]++;
                    }
                }
            }
        }
    }

    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j){
            cout<<ans[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

🔹 题目二:灌溉

题目描述

小蓝负责花园的灌溉工作。

花园可以看成一个 n m 列的方格图形。中间有一部分位置上安装有出水管。

小蓝可以控制一个按钮同时打开所有的出水管,打开时,有出水管的位置可以被认为已经灌溉好。

每经过一分钟,水就会向四面扩展一个方格,被扩展到的方格可以被认为已经灌溉好。即如果前一分钟某一个方格被灌溉好,则下一分钟它上下左右的四个方格也被灌溉好。

给定花园水管的位置,请问 k 分钟后,有多少个方格被灌溉好?
输入描述

输入的第一行包含两个整数 n, m

第二行包含一个整数 t ,表示出水管的数量。

接下来 t 行描述出水管的位置,其中第 i 行包含两个数 r, c ,表示第 r 行第 c 列有一个排水管。

接下来一行包含一个整数 k 。其中, 。 其中, 。其中, 1 \\leq n, m \\leq 100, 1 \\leq t \\leq 10, 1 \\leq k \\leq 100
输出描述

输出一个整数,表示答案。
输入输出样例

复制代码
输入
3 6
2
2 2
3 4
1

输出
9

✅ 我的代码

cpp 复制代码
#include <iostream>
using namespace std;

const int N = 105;
int a[N][N];

int main() {
    int m, n;
    cin >> m >> n;

    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            a[i][j] = 0;
        }
    }

    int t;
    cin >> t;
    for (int i = 1; i <= t; ++i) {
        int x, y;
        cin >> x >> y;
        a[x][y] = 1;
    }

    int k;
    cin >> k;

    for (int minute = 0; minute < k; ++minute) {
        int temp[N][N] = {0};

        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                temp[i][j] = a[i][j];
            }
        }

        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (a[i][j]) {
                    if (i - 1 >= 1) temp[i - 1][j] = 1;
                    if (j - 1 >= 1) temp[i][j - 1] = 1;
                    if (i + 1 <= m) temp[i + 1][j] = 1;
                    if (j + 1 <= n) temp[i][j + 1] = 1;
                }
            }
        }

        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                a[i][j] = temp[i][j];
            }
        }
    }

    int count = 0;
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            count += a[i][j];
        }
    }

    cout << count;
    return 0;
}

🔹 题目三:回文日期

题目描述

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

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

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

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

输入描述

输入包含一个八位整数 N ,表示日期。对于所有评测用例, ,表示日期。 对于所有评测用例, ,表示日期。对于所有评测用例, 10000101 \\leq N \\leq 89991231 ,保证 N 是一个合法日期的 8 位数表示。
输出描述

输出两行,每行 1 个八位数。第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。
输入输出样例

复制代码
输入
20200202

输出
20211202
21211212

运行限制

  • 最大运行时间:1s
  • 最大运行内存:256M

✅ 我的代码

cpp 复制代码
#include <iostream>
using namespace std;

bool isleap(int y) { //判断闰年
    return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); //闰年可以被4整除但不能被100整除,或能被400整除。
}

bool check(int year, int month, int day) { //判断日期是否合法
    if (month > 12 || month == 0) return false;
    if (day > 31) return false;
    if (month == 2) {
        if (isleap(year) && day > 29)
            return false;
        if (!isleap(year) && day > 28)
            return false;
    }
    if (month == 4 || month == 6 || month == 9 || month == 11)
        if (day > 30) return false;
    return true;
}

int main()
{
    int n, i;
    cin >> n;

    int a, b, c, d, e, f, g, h;
    int year, month, day;
    bool flag = false;

    for (i = n + 1; i <= 99999999; ++i) {
        year = i / 10000;
        month = i % 10000 / 100;
        day = i % 100;
        a = year / 1000;
        b = year / 100 % 10;
        c = year % 100 / 10;
        d = year % 10;
        e = month / 10;
        f = month % 10;
        g = day / 10;
        h = day % 10;

        if (a == h && c == f && b == g && e == d && flag == false) { //该部分目的是输出第一个回文日期,flag作为标记。
            //当找到第一个回文日期之后,将flag变为true。这样下一次碰到普通回文日期时就不会输出。
            if (check(year, month, day)) {
                cout << i << endl;
                flag = true;
            }
        }

        if (a == h && c == f && b == g && e == d && a == c && b == e) { //输出ABABBABA型的回文日期
            if (check(year, month, day)) {
                cout << i << endl;
                break;
            }
        }
    }
    return 0;
}

🌟 我的思考

这三道题,虽然看起来完全不同,但都用了模拟的思想:

  • 扫雷:模拟每个格子的邻居统计过程,遍历所有位置,对每个非雷格子计算周围雷数。
  • 灌溉 :模拟水从初始点开始每分钟向四周扩散的过程,用 temp 数组避免当前状态干扰。
  • 回文日期:模拟从当前日期往后逐天枚举,直到找到满足条件的回文日。

你会发现:

模拟的本质不是"暴力",而是"还原过程"

只要你能把问题拆解成"每一步做什么",就能写出正确的代码。哪怕数据范围大一点,只要逻辑正确,也能通过。

而且,很多高级算法(如 BFS、DFS、动态规划)都是建立在"模拟"的基础上的。先学会"一步一步走",才能学会"跳着走"。


✅ 总结

  • 模拟 ≠ 暴力,它是过程还原
  • 要清晰理解问题的每一步操作
  • 可以用辅助数组避免状态污染(如灌溉题)
  • 枚举 + 判断 + 循环 = 模拟的核心三件套
  • 先求正确,再求高效

如果你也在刷算法题,不妨试试今天这三道题,用最直白的模拟方法做一遍。

有时候,慢一点,反而更快。

欢迎在评论区贴出你的解法,我们一起交流进步!👇


相关推荐
Moonquakes5402 小时前
嵌入式开发基础学习笔记(pwm spi)
笔记·学习
●VON2 小时前
React Native for OpenHarmony:解构 TouchableOpacity 的触摸反馈与事件流控制
javascript·学习·react native·react.js·性能优化·openharmony
EQ-雪梨蛋花汤2 小时前
【问题反馈】JNI 开发:为什么 C++ 在 Debug 正常,Release 却返回 NaN?
开发语言·c++
Blossom.1182 小时前
从“金鱼记忆“到“超级大脑“:2025年AI智能体记忆机制与MoE架构的融合革命
人工智能·python·算法·架构·自动化·whisper·哈希算法
王老师青少年编程2 小时前
2023信奥赛C++提高组csp-s复赛真题及题解:密码锁
c++·真题·csp·密码锁·信奥赛·csp-s·提高组
金枪不摆鳍2 小时前
算法-贪心算法
算法·贪心算法
naruto_lnq2 小时前
高性能消息队列实现
开发语言·c++·算法
charlie1145141912 小时前
malloc 在多线程下为什么慢?——从原理到实测
开发语言·c++·笔记·学习·工程实践
池央2 小时前
贪心算法-摆动序列
算法·贪心算法