更新"太及时"引发的错

【问题的出现】

  C++一本通在线测评"1191:流感传染",题目要求如下

【题目描述】
有一批易感人群住在网格状的宿舍区内,宿舍区为n*n的矩阵,每个格点为一个房间,房间里可能住人,也可能空着。在第一天,有些房间里的人得了流感,

以后每天,得流感的人会使其邻居传染上流感,(已经得病的不变),空房间不会传染。请输出第m天得流感的人数。 【输入】 第一行一个数字n,n不超过100,表示有n*n的宿舍房间。 接下来的n行,每行n个字符,’.’表示第一天该房间住着健康的人,’#’表示该房间空着,’@’表示第一天该房间住着得流感的人。 接下来的一行是一个整数m,m不超过100。 【输出】 输出第m天,得流感的人数。
【题目分析】

  这个题目其实很简单的,就是遍历数组,如果某个房间里住着得流感的人,就去查看其邻居是否有人,有则让人得上流感(可以不考虑是否己是流感患者),于是得到如下代码

//1191:流感传染
#include<iostream>
using namespace std;
char a[102][102];
int n,m,ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)//输入方阵数据
        for(int j=1;j<=n;j++)cin>>a[i][j];
    cin>>m;
    for(int i=2;i<=m;i++)//每天更新人群状态 
        for(int j=1;j<=n;j++)//遍历整个数组 
            for(int k=1;k<=n;k++)
            {
                if(a[j][k]=='@')//如果当前房间里有患流感的人
                {
                    if(a[j-1][k]=='.')a[j-1][k]='@';//邻居若有人,则让其患上流感 
                    if(a[j+1][k]=='.')a[j+1][k]='@';
                    if(a[j][k-1]=='.')a[j][k-1]='@';
                    if(a[j][k+1]=='.')a[j][k+1]='@';
                 } 
             }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
            if(a[i][j]=='@')ans++;
    cout<<ans;
    return 0;
}
【初次表现】

  程序运行,输入样例数据,

5
....#
.#.@.
.#@..
#....
.....
4

  结果显示18(正确结果16)

【错因分析】

  细细分析出错原因在哪里?--数据更新太过及时.在第一天中,患流感的人只有a[2][4]和a[3][3],a[3][4]是没有患流感的,也就意味着在第二天a[3][5]和a[4][4]应该还是健康的,但通过上述程序的运行,情况变得不一样了.当j=2,k=4时显然a[2][4]是流感患者,他会让a[3][4]以a[2][5]等四人患上流感,同样在第二天中,当j=2,k=5时,第一天的数据中a[2][5]是健康人,不应该有传染性,但由于上一循环中(j=2,k=4)己把a[2][5]修改为@了,就具有传染性了,这就出错了.同样,a[3][4],a[4][3]也会出现类似错误.

【解决办法】

  办法一:增加一个维度(天数),让两天的数据互不干扰.先把前天的数据复制到当前天,再根据前一天的数据更新被传染人员,代码如下

//1191:流感传染
#include<iostream>
using namespace std;
char a[102][102][102];//第一维度代表天数 
int n,m,ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)cin>>a[1][i][j];
    cin>>m;
    for(int i=2;i<=m;i++)//每天更新人群状态 
    {
        for(int j=1;j<=n;j++)//复制前一天的数据 
            for(int k=1;k<=n;k++)
                a[i][j][k]=a[i-1][j][k];
                 
        for(int j=1;j<=n;j++)//遍历整个数组 
            for(int k=1;k<=n;k++)
            {
                if(a[i-1][j][k]=='@')//如果当前房间里有患流感的人
                {
                    if(a[i-1][j-1][k]=='.')a[i][j-1][k]='@';//邻居若有人,则让其患上流感 
                    if(a[i-1][j+1][k]=='.')a[i][j+1][k]='@';
                    if(a[i-1][j][k-1]=='.')a[i][j][k-1]='@';
                    if(a[i-1][j][k+1]=='.')a[i][j][k+1]='@';
                 } 
             }        
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
            if(a[m][i][j]=='@')ans++;
    cout<<ans;
    return 0;
}

  办法二:让新感染者数据缓冲一天.新感染者的值不直接换成@,而是换成除.和@,#外的字符,当循环正常读到该天数据时再替换为@,代码如下

//1191:流感传染
#include<iostream>
using namespace std;
char a[102][102];
int n,m,ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)cin>>a[i][j];
    cin>>m;
    for(int i=2;i<=m;i++)//每天更新人群状态 
        for(int j=1;j<=n;j++)//遍历整个数组 
            for(int k=1;k<=n;k++)
            {
                if(a[j][k]=='@')//如果当前房间里有患流感的人,邻居若有人,则让其患上流感 
                {
                    if(a[j-1][k]=='.')a[j-1][k]='@';//前两行数据己读过了,直接替换不会出错 
                    if(a[j][k-1]=='.')a[j][k-1]='@';
                    if(a[j+1][k]=='.')a[j+1][k]='h';//先标记为感染而不传染 
                    if(a[j][k+1]=='.')a[j][k+1]='h';
                 }
                if(a[j][k]=='h')a[j][k]='@';//己感染人员标记为感染且可传染 
             }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
            if(a[i][j]=='@')ans++;
    cout<<ans;
    return 0;
}
【话题延伸】

  这一现象也正是在背包问题中,0-1背包和完全背包处理方式只是一个顺序就能改变的原因之一.

上一篇:第102题


下一篇:API-6_1 初识Web API