【信息奥赛题解】流感传染(详细题解 & C++代码)

📚 流感传染

🚀 题目浏览

【题目名称】流感传染
【题目描述】

有一批易感人群住在网格状的宿舍区内,宿舍区为 n ∗ n n*n nn 的矩阵,每个格点为一个房间,房间里可能住人,也可能空着。

在第一天,有些房间里的人得了流感,以后每天,得流感的人会使其邻居传染上流感(已经得病的不变),空房间不会传染。

请输出第 m m m 天得流感的人数。

【输入】

第一行一个数字 n n n n n n 不超过 100 100 100,表示有 n ∗ n n*n nn 的宿舍房间。

接下来的 n n n 行,每行 n n n 个字符,’.’ 表示第一天该房间住着健康的人,’#’ 表示该房间空着,’@’ 表示第一天该房间住着得流感的人。

接下来的一行是一个整数 m m m m m m 不超过 100 100 100

【输出】

输出第 m m m 天,得流感的人数。

【输入样例】
5
....#
.#.@.
.#@..
#....
.....
4
【输出样例】
16
【原题链接】

http://ybt.ssoier.cn:8088/problem_show.php?pid=1191


☘️ 题解分析

推荐前往摊主的个人博客查看该题解,享受更好的阅读体验(且由于时间精力有限,摊主发现文章错误后一般只在个人博客中修改,这里的文章版本可能不是最新的)
主站点:信息奥赛题解|流感传染
备用站点:信息奥赛题解|流感传染

在博主看来,本题对于 算法初学者 主要有以下几个 难点

  • 如何读入字符矩阵 ?
  • 如何模拟感染过程 ?

对于第一个难点,博主采用的方式是 「定义一个 二维的字符数组、使用 cin 读入数据」(需要开启 cin 读入优化,减少读入数据的时间)。

const int N = 105;
char s[N][N];  // 定义一个二维的字符数组

int main(){
    //cin 读入优化
    ios::sync_with_stdio(false);
    cin.tie(0);
  
    //读入矩阵
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> s[i][j];
        }
    }
    return 0;
}

🍉 PS:使用 scanf 读入时,需要考虑 换行符的读入,而 cin 则不用,这是 scanf 的缺点;但 scanf 的读入速度比没有开 cin 读入优化的 cin 快很多,这是 scanf 的优点。

cin 在开了 cin 读入优化后,速度基本上能达到 scanf 的水平,此题完全够用了。


对于第二个难点,博主采用了 遍历模拟 的方法,每天都遍历一次二维矩阵,模拟感染过程。(本题应该也可以使用 广度优先搜索 BFS 的方法,等博主研究完 「搜索」专题 后再来补充)🧑🏻‍💻

模拟过程也非常简单,只需在遍历时,遇到 ‘@’ 的房间,就把四周非空的房间置为 ‘@’

❗️❗️❗️但是这种方式非常容易出现 一个误区,就是 「每次矩阵还没有遍历完,就急着把 当前房间感染的新房间置为 ‘@’

这样会出现下面这种情况:

  • 第一天,初始情况
@..
#..
  • 第二天,遍历到第 1 个房间时
@@.
#..
  • 第二天,遍历到第 2 个房间时,由于当前房间被置为了 ‘@’,所以其周围的房间也被置为 ‘@’
@@@
#@.
  • 第二天,遍历到第 3 个房间时,由于当前房间被置为了 ‘@’,所以其周围的房间也被置为 ‘@’
@@@
#@@
  • … 省略剩下的遍历过程

  • 第二天的最终结果

@@@
#@@

这样的得到的结果显然是不对的 ❌

因为实际上第 2 天,仅有 2 号房间被感染,3 号房间需要等到第 3 天才会被感染。✅

  • 第二天实际结果
@@.
#..

所以,我们需要借助一个标记数组 b,存储当天被感染的房间,而不是在遍历时直接修改原矩阵的值。

等当天的 遍历结束后,再根据标记数组 b 来修改原矩阵的值,这样就不会出现上面这种情况了。

从代码的角度,是这样的区别:

//模拟感染过程,并标记
for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
        if (s[i][j] == '@') {
            // 错误的直接修改法
            //if (s[i - 1][j] != '#') s[i - 1][j] = '@';
            //if (s[i + 1][j] != '#') s[i + 1][j] = '@';
            //if (s[i][j - 1] != '#') s[i][j - 1] = '@';
            //if (s[i][j + 1] != '#') s[i][j + 1] = '@';
          
            // 正确的标记法
            if (s[i - 1][j] != '#') b[i - 1][j] = 1;
            if (s[i + 1][j] != '#') b[i + 1][j] = 1;
            if (s[i][j - 1] != '#') b[i][j - 1] = 1;
            if (s[i][j + 1] != '#') b[i][j + 1] = 1;
        }
    }
}

//将带有标记的房间感染
for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
        if (b[i][j] == 1) {
            s[i][j] = '@';
        }
    }
}

🧑🏻‍💻 C++ 代码

#include<bits/stdc++.h>

using namespace std;
const int N = 105;

char s[N][N];  // 存储房间状态
int b[N][N];  //用于感染的标记数组
int n, m, cnt;

// 每天更新矩阵
void update();

// 统计并输出感染人数
void print();

int main() {
    ios::sync_with_stdio(false);  // cin读入优化
    cin.tie(0);

    //读入矩阵
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> s[i][j];
        }
    }

    // 统计第m天,需要更新m-1次
    cin >> m;
    while (--m) {
        //更新矩阵
        update();
    }

    // 统计感染总人数
    print();

    return 0;
}


void update() {
    //初始化感染标记数组b
    memset(b, 0, sizeof(b));

    //模拟感染过程,并标记
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            // 向四个方向进行感染,标记位置
            // 注意不能马上把相应房间改为 @,否则会出现一条线上的房间一天就感染完等情况
            if (s[i][j] == '@') {
                if (s[i - 1][j] != '#') b[i - 1][j] = 1;
                if (s[i + 1][j] != '#') b[i + 1][j] = 1;
                if (s[i][j - 1] != '#') b[i][j - 1] = 1;
                if (s[i][j + 1] != '#') b[i][j + 1] = 1;
            }
        }
    }

    //将带有标记的房间感染
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (b[i][j] == 1) {
                s[i][j] = '@';
            }
        }
    }
}

void print() {
    cnt = 0;
    //统计感染人数
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (s[i][j] == '@')
                cnt++;
        }
    }
    cout << cnt << endl;
}

image-20221215230303585


✍️ 写在最后

如果各位小伙伴觉得博主的题解写的不错,可以给本文 点个赞 👍

这样可以让 更加优质的文章更大的概率 被推送到 搜索界面的榜首,为未来的小伙伴们节约更多搜索、阅读的成本。 🚀

同时,你的支持 也是我 不断创作 的动力。☘️

有想要看更多题解报告的小伙伴,也可以关注我的专栏「信息奥赛题解」。

我们下期再见。👋

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值