华为OD机试 - 疫情扩散时间计算 - 广度优先搜索(Java 2024 C卷 200分)

这篇博客介绍了华为OD机试中的一道题目,涉及矩阵算法和Java编程。问题核心是计算病菌在N*N地图上扩散至全部区域所需的天数,特殊情况下返回-1。博主提供了详细的解题思路、Java代码实现以及输入输出示例,帮助读者理解并解决此类问题。

一、题目描述

在一个地图中(地图有N*N个区域组成),有部分区域被感染病菌。

感染区每天都会把周围(上下左右)的4个区域感染。

请根据给定的地图计算,多少天后,全部区域都会被感染。 如果初始地图上所有区域全部都被感染,或者没有被感染,返回-1

二、输入描述

一行N*N个数字(只包含0,1,不会有其他数字)表示一个地图,数字间用“,”分割,0表示未感染区域,1表示已经感染区域
每N个数字表示地图中一行,输入数据共表示N行N列的区域地图。 例如输入1,0,1,0,0,0,1,0,1,表示地图

1, 0, 1
0, 0, 0
1, 0, 1

三、输出描述

一个整数,表示经过多少天后,全部区域都被感染1 <=N <= 200

四、解题思路

  1. 将输入字符串转换为一维数组;
  2. 将一维数组转换为二维矩阵;
  3. 定义感染区域队列arrQueue;
    • 将一维数组转换为二维矩阵;
    • 将感染区域加入队列;
  4. 判断特殊情况;
  5. 记录未感染区域数量;
  6. 记录四个方向的偏移量;
  7. 记录感染天数;
  8. 当队列不为空且还有未感染区域时,进行循环;
    • 取出队首元素;
    • 获取队首元素的坐标;
<think>好的,我现在需要帮助用户获取解决疫情传播模拟问题的C++代码。用户明确提到需要一个n*n矩阵的感染病毒扩散算法。首先,我得回忆一下相关的知识,并结合提供的引用内容来构建解决方案。 首先,用户提供的引用中有几个提到了华为OD题,特别是关于计算疫情扩散时间的实现。引用[3]、[4]、[5]都涉及C++的实现,这可能对用户有帮助。这些引用中的思路通常是使用广度优先搜索(BFS)来模拟每天的感染扩散,因为BFS适合处理层级传播的问题,比如病毒每天扩散到周围区域。 接下来,我需要考虑如何将这个问题转化为代码。基本思路应该是: 1. **输入处理**:将输入的矩阵转换为二维数组,并记录初始的感染区域。可能需要处理输入的字符串,例如用逗号隔的数字,转换成二维数组。例如,输入“1,0,1,0,0,0,1,0,1”对应3x3矩阵。 2. **初始状态检查**:根据引用中的示例,如果初始时全部区域已感染或没有感染区域,返回-1。这一步需要在代码开始时就处理,避免不必要的计算。 3. **BFS初始化**:将初始已感染的坐标加入队列,并记录天数。同时需要记录每个区域被感染的时间,或者使用一个访问矩阵来跟踪哪些区域已经被感染。 4. **扩散过程模拟**:每天处理当前队列中的所有节点,向四个方向扩散,将新感染的区域加入队列,并标记为已感染。每处理完一天的所有节点,天数加一。需要注意的是,同一天感染的区域应该同时处理,因此需要使用层序遍历的方式,每次处理当前层的所有节点后再进入下一层。 5. **终止条件**:当所有区域都被感染时,返回天数。如果队列为空但还有未感染的区域,说明无法全部感染,返回-1。不过根据问题描述,可能不存在这种情况,因为只要有感染源,理论上会扩散到整个矩阵,但需要验证边界条件。 接下来,我需要考虑代码的结构。大致步骤包括: - 解析输入字符串为二维矩阵- 统计初始感染区域和健康区域的数量。 - 如果健康区域数量为0,或者初始感染区域数量为0,返回-1。 - 使用队列进行BFS,记录每个节点的时间- 每次扩散时检查四个方向,确保不越界且未被感染。 - 每次处理完一天的所有感染节点后,天数增加。 - 最终判断是否所有区域都被感染,如果是,返回天数,否则-1。 需要注意的是,在BFS中,每个新感染的区域应该被立即标记,以免重复处理。例如,在扩散时,如果某个相邻区域是健康的,就将其标记为已感染,并加入队列,这样后续的扩散不会重复处理该区域。 现在,参考引用中的示例代码,例如引用[3]的C++实现,可能使用队列来存储感染点的坐标,以及记录天数。例如,使用一个结构体或pair来保存坐标和当前天数,或者使用层序遍历,每次处理完当前层的所有节点后,天数加一。 可能的问题点包括输入处理是否正确,特别是将用户输入的一维数组转换为二维矩阵。例如,输入是逗号隔的字符串,需要先割成数组,然后按行填充到n*n的矩阵中。 另外,关于性能,因为n的范围是1到200,BFS的时间复杂度是O(n^2),这在n=200时是可以接受的,不会有性能问题。 现在,编写代码的大致步骤: 1. **输入处理**:读取输入字符串,割成数字数组,确定n的值(sqrt(数组长度)),然后填充到二维数组。 2. **初始检查**:统计初始感染数和健康数。如果健康数为0或者感染数为0,返回-1。 3. **BFS队列初始化**:遍历矩阵,将所有初始感染点加入队列,并记录它们的坐标。同时,创建一个visited矩阵来标记是否已感染,或者直接修改原矩阵的值(例如将0变为1,并记录天数)。 4. **扩散循环**:每次处理队列中当前层的所有节点(记录当前队列大小,然后循环处理这么多次),然后处理每个节点的四个方向。对于每个方向,检查是否在矩阵范围内,且未被感染。如果是,则标记为已感染,加入队列,并减少健康计数。 5. **检查完成条件**:每次处理完一天后,检查健康计数是否为0。如果是,返回当前天数。 6. **处理特殊情况**:如果队列为空但健康计数还未归零,说明有无法到达的区域,返回-1。但根据问题描述,这种情况是否可能?例如,矩阵中有多个隔离区域,初始感染无法扩散到所有区域。例如,如果初始感染在左上角,而右下角有一个无法到达的区域,那么需要返回-1。但根据引用中的示例,可能题目假设只要存在感染源,最终会扩散到整个矩阵,或者用户的需求是否需要处理这种情况?根据引用[5]的描述,如果初始地图全部感染或者无感染区域返回-1,其他情况应该可以扩散到全部。不过实际中可能存在无法全部感染的情况,比如感染源被隔离。因此代码需要考虑这种情况,即当BFS结束后,健康计数是否大于0,此时返回-1。 现在,结合这些思考,开始编写代码: 首先处理输入,例如输入字符串“1,0,1,0,0,0,1,0,1”转化为3x3的矩阵。可以用split函数割字符串,然后转换为整数数组。然后确定n的值,例如数组长度是9,则n=3。 然后统计初始感染点,加入队列,并计算健康区域的数量。例如,初始感染区域是值为1的点,健康区域是值为0的点。如果健康区域数量为0,或者初始感染区域数量为0,返回-1。 然后进行BFS。队列中保存的是坐标对(i,j),以及可能需要记录天数。但通常使用层序遍历,每次处理完一层(即一天的所有感染源)后,天数加一。 例如,初始队列中有所有感染点。第一天处理这些点,扩散到周围四个方向,将新感染的点加入队列。这些新点将在第二天处理,所以每次处理完当前层后,天数加一。但初始天数为0,处理完初始队列后,第一天扩散,所以天数可能需要调整。 例如,初始时所有感染点的天数是0。当处理它们的扩散时,新感染点的天数是当前天数+1。但可能需要在处理完所有初始节点后,天数增加。例如: 初始队列中的所有节点是第0天。处理它们时,扩散到周围,这些新节点属于第1天。此时,当处理完所有初始节点后,天数变为1。如果在处理完这些后健康区域已经为0,则返回1。 但根据示例1,输入中的初始状态是四个角落被感染,中间是0。第一天扩散后,周围的四个方向被感染,但中间仍然未被感染。第二天中间被感染,所以输出2。这说明初始处理后的天数需要正确计算。 因此,正确的做法是,初始时将所有感染点加入队列,并设置天数为0。然后,每次处理完当前队列中的所有节点(即当前天的所有感染源),将天数加一,并将新感染的节点加入队列。这样,第一天处理初始感染源的扩散,生成的新感染属于下一天。 或者,初始天数为-1,处理完所有初始节点后,天数加一,得到0,然后处理新感染的节点,天数再加一。需要根据示例调整。 比如示例1中的输入: 初始感染点是四个角落(坐标(0,0),(0,2),(2,0),(2,2)),中间是0。第一天扩散后,这四个角落的周围会被感染,即四个边的中间点(如(0,1), (1,0), (1,2), (2,1))会被感染。此时,中间点(1,1)仍然未被感染。第二天,这四个新感染的点会扩散到中间点,此时中间点被感染,所以总天数是2。 所以,在代码中,处理初始队列时,天数应该是0,然后处理完初始队列的扩散后,天数变为1,此时是否完成?需要看健康区域的数量是否归零。例如,在第一天处理后,健康区域可能减少到1(中间点),第二天处理后归零,总天数是2。 因此,在代码中,初始队列中的感染点处理完后,第一天扩散到周围,此时健康区域减少,天数加一。然后在处理完第一天的扩散节点后,第二天再次扩散,天数再加一,此时健康区域归零,返回天数。 这可能需要将天数的初始值设为0,然后在每次处理完一层后,如果还有新的感染节点,天数加一。或者,将初始天数设为-1,处理完初始队列后,天数变为0,然后在每次处理完一层后,天数加一。 例如: 初始队列中有k个节点,处理它们时,扩散到周围,此时如果有新节点加入队列,那么天数增加。初始天数为0,处理完初始队列后,如果新节点加入,则天数变为1。例如,初始处理的是第0天的节点,处理后的扩散属于第1天。 所以,正确的处理方式是: - 初始队列中的节点属于第0天,处理它们的扩散,将新节点加入队列。这些新节点属于第1天。 - 每处理完一层的所有节点,天数加一。初始时天数为0,处理完初始队列后,如果新节点被加入,则天数变为1。 - 当健康区域归零时,返回当前天数。 或者,使用一个变量记录时间,初始为0。每次处理完当前队列中的所有节点(当前层的节点)后,时间加一。但需要注意的是,初始的感染节点不需要时间,因为它们已经是感染状态。扩散是从第二天开始的。 例如,在示例1中: 初始队列中有4个节点(四个角),此时健康区域数为1(中间点)。 处理这四个节点时,检查它们的四个方向。比如,对于节点(0,0),检查上下左右。上边越界,下边是(1,0),右边是(0,1)。假设这些方向在初始时是0,那么会被感染,并加入队列。但此时处理完初始队列的所有节点后,时间增加1,变成1天。此时健康区域数减少到0吗?或者需要计算新的感染数? 可能需要在每次扩散后更新健康区域的数量。例如,每感染一个区域,就将健康计数减一。当健康计数为0时,返回当前天数。 例如,初始健康计数是初始矩阵中的0的数量。在示例1中,初始0的数量是5(中间点和四个边的中间点?或者需要具体计算输入数组)。 例如,输入示例1的数组是1,0,1,0,0,0,1,0,1,对应的3x3矩阵是: 1 0 1 0 0 0 1 0 1 初始时,感染区域是四个角落的1,其他位置是0。健康计数为5(中间四个0?或者总共有5个0?需要数一下具体输入)。 原输入数组长度是9,其中1的数量是4(位置0,2,6,8),0的数量是5。所以初始健康计数是5。 当处理初始队列中的四个感染点,扩散到四个方向: 例如,对于(0,0),周围是(0,1)(1,0)。这两个位置在初始时是0,所以会被感染,健康计数减少2,变为3。然后将这两个点加入队列。 同样,处理(0,2)节点,扩散(0,1)(1,2)。但(0,1)已经被感染了吗?在BFS中,需要立即标记已感染,否则同一个点可能被多次处理。所以,在扩散过程中,当处理一个节点时,检查四个方向,如果某个方向是0,则将其标记为已感染(例如设为1),并将该点加入队列,同时健康计数减一。这样,同一层的其他节点在处理时,不会重复感染同一个点。 例如,处理初始队列中的四个节点: 处理(0,0)时,检查四个方向: 上:越界,忽略。 下:(1,0),原为0,感染,健康计数减一,设为1,加入队列。 左:越界,忽略。 右:(0,1),原为0,感染,健康计数减一,设为1,加入队列。 处理(0,2)时: 上:越界。 下:(1,2),原为0,感染,健康计数减一,设为1,加入队列. 左:(0,1)已经被感染,所以忽略。 右:越界. 处理(2,0)时: 上:(1,0),原为0,感染,减一,设为1,加入队列. 下:越界. 左:越界. 右:(2,1),原为0,感染,减一,设为1,加入队列. 处理(2,2)时: 上:(1,2),已经被感染?假设之前(0,2)的处理已经感染了(1,2),或者此时是否已经被处理? 这里可能存在顺序问题。如果使用层序遍历,初始队列中的四个节点都在同一层(第0天),处理时依次处理每个节点,并同时标记已感染。例如,在处理(0,0)时,(1,0)(0,1)被感染并标记。然后处理(0,2)时,其下方的(1,2)被感染,标记,并加入队列。同样,处理(2,0)时,(1,0)可能已经被感染?或者原矩阵中的(1,0)初始是0,现在被感染了吗? 这里需要明确的是,在BFS中,每个节点被处理时,会扩散到周围,并立即将周围节点标记为已感染。因此,当处理完(0,0)后,(1,0)(0,1)已经被标记为已感染。当处理(2,0)时,它的上方节点是(1,0),此时已经被感染,所以不会再次处理。 因此,初始队列中的四个节点处理完毕后,新感染的节点是四个边的中间点((0,1), (1,0), (1,2), (2,1))以及中间的(1,0)可能被多次处理?或者实际上这四个中间边点会被感染? 比如,在示例1的初始矩阵中: 1 0 1 0 0 0 1 0 1 处理四个角点: - (0,0)处理时,感染(0,1)(1,0)- (0,2)处理时,感染(0,1)(已经感染)和(1,2)- (2,0)处理时,感染(1,0)(已经感染)和(2,1)- (2,2)处理时,感染(2,1)(已经感染)和(1,2)(已经感染)。 所以,新感染的节点是(0,1), (1,0), (1,2), (2,1)。这四个点被加入队列,健康计数从5减少到5-4=1(中间的0还在)。 此时,天数增加1,变为1天。此时健康计数是1,未达到0,继续处理队列中的四个新节点。 处理这四个新节点: 对于(0,1): 检查四个方向:上越界,下(1,1)(0),左(0,0)已感染,右(0,2)已感染。所以感染(1,1),健康计数减一,变为0,加入队列。 对于(1,0): 检查四个方向:上(0,0)已感染,下(2,0)已感染,左越界,右(1,1)(0)。感染(1,1),但已经被处理?或者在处理(0,1)时已经感染,所以此时健康计数可能已经为0。 这里需要明确的是,每个新感染的节点在处理时,会扩散到周围。例如,处理(0,1)时,检查到(1,1)未被感染,感染它,健康计数减到0。然后处理(1,0)时,检查其右边的(1,1),此时已经被感染,所以不会重复处理。 因此,在处理这四个新节点的过程中,当处理到第一个节点(比如(0,1))时,其下方(1,1)被感染,健康计数变为0。此时,其他节点在处理时可能不会有新的感染,但队列中已经加入了(1,1)。处理完这四个节点后,队列中有(1,1)这个节点,此时天数增加1,变为2天。此时,健康计数已经是0,所以返回天数2。 因此,代码的正确流程是: - 初始队列中的四个节点,处理完后,健康计数从5变为1(中间点)。 - 第一天处理这四个新感染的节点,此时扩散到中间点,健康计数变为0。 - 天数此时是1,但处理完这四个节点后,天数增加1变为2,然后检查健康计数是否为0,如果是则返回2。 或者,在每次处理完一层后,天数加一,但此时是否应该先检查健康计数? 例如,处理初始队列(四个节点)后,健康计数是5-4=1。此时,天数还是0?或者天数变为1? 这里可能有一个逻辑错误。需要明确,处理初始队列中的节点属于第0天,扩散后的新节点属于第1天。因此,在处理完初始队列中的节点后,天数变为1天。然后处理新队列中的四个节点,处理完后,天数变为2天,此时健康计数为0,返回2。 但根据示例1的输出,处理完初始队列后,第一天扩散到四个边中间点,此时健康计数是1。第二天,处理这四个边中间点,扩散到中间点,健康计数变为0,所以总天数是2。 因此,正确的处理方式是: - 初始队列处理完后,天数增加1(第一天)。 - 处理新队列中的四个节点,处理完后,天数增加1(第二天),然后检查健康计数是否为0。 或者,在处理每一层之前,天数已经加一。例如: 初始队列不为空,天数初始为0。然后,进入循环,处理当前队列中的所有节点: int days = 0; while (!q.empty()) { int size = q.size(); bool infected = false; for (int i = 0; i < size; ++i) { // 处理节点,扩散 // 如果有新感染,infected = true } if (infected) days++; } 但这种方法可能不太准确,或者需要调整。 更标准的方式是使用层序遍历,每处理完一层(即一天的所有扩散),天数加一。初始队列中的节点是第0天的,处理它们扩散到周围,此时新感染的节点属于第1天。因此,处理完初始队列后,天数加一。 例如: int days = -1; while (!queue.empty()) { int levelSize = queue.size(); days++; for (int i = 0; i < levelSize; ++i) { auto [x, y] = queue.front(); queue.pop(); // 扩散到四个方向 for 每个方向: if 可感染: mark as infected queue.push(new node) healthyCount-- } } return days; 但这样,初始队列中的节点处理完后,days为0,然后处理新感染的节点,days变为1,依此类推。例如,在示例1中: 初始队列处理完后,days=0。此时,健康计数是1。然后处理新队列中的四个节点,此时days增加到1。在处理这四个节点时,感染中间点,健康计数减到0。此时,循环继续,处理下一层(中间点),此时days增加到2。但此时健康计数已经为0,是否需要立即返回? 可能需要调整逻辑,在每次处理完一层后,检查健康计数是否为0。如果是,返回当前days。 或者,在每次扩散后立即检查健康计数是否为0,如果是,则返回days+1?这需要更仔细的处理。 例如,初始队列处理完后,days=0。此时,健康计数是5-4=1。然后处理下一层(四个节点),此时在循环中,days增加到1。在处理这四个节点时,会感染中间点,健康计数变为0。此时,在完成这一层的处理后,检查健康计数是否为0,如果是,返回days=1? 但示例1的输出是2。这说明这里的逻辑可能有问题。 这暗示了代码中的天数计算可能存在错误,需要重新考虑。 正确的处理方式是,每处理完一层,天数加一,并且在该层处理的过程中可能会减少健康计数。因此,在完成一层的处理后,检查健康计数是否为0,如果是,返回当前的天数。 例如,在示例1中: 初始队列中有四个节点(第0层)。处理这一层时,扩散到四个边中间点,健康计数从5减少到1。此时,处理完这一层后,天数加一(变为1天)。然后检查健康计数是否为0?不是,继续处理下一层。 下一层队列中有四个边中间点。处理这四个点时,扩散到中间点,健康计数减少到0。处理完这一层后,天数加一(变为2天)。此时检查健康计数为0,返回2天。 因此,正确的代码逻辑应该是在每次处理完一层后,天数加一,并在此时检查健康计数是否为0。如果是,返回当前的天数。 因此,代码的大致结构应该是: 初始化队列,健康计数等。 如果健康计数为0,返回-1。 int days = 0; while (!queue.empty()) { int levelSize = queue.size(); bool hasNewInfected = false; for (int i = 0; i < levelSize; ++i) { 处理当前节点,扩散到四个方向。 如果有新感染,hasNewInfected = true,并减少健康计数。 } if (hasNewInfected) { days++; } if (healthyCount == 0) { return days; } } // 如果队列为空但健康计数>0,返回-1 return -1; 但是,这种结构可能无法正确处理天数,例如,在示例1中,处理完初始队列后,hasNewInfected为true,days变为1。此时健康计数是1。然后处理下一层,扩散到中间点,健康计数变为0,days变为2。此时返回2。 这可能符合示例的要求。但需要仔细验证。 另一个可能的代码结构是,将天数的更新放在处理完一层之后,无论是否有新感染。例如: int days = 0; while (!queue.empty() && healthyCount > 0) { int levelSize = queue.size(); for (int i = 0; i < levelSize; ++i) { 处理节点,扩散到四个方向。 如果有新感染,减少健康计数。 } days++; } return healthyCount == 0 ? days : -1; 但这样,在示例1中,初始队列处理完后,days变为1。然后处理下一层,days变为2。此时健康计数为0,返回2。这符合示例的要求。 因此,正确的代码结构应该是: 初始化队列,健康计数。 如果健康计数是0,返回-1. 如果初始队列为空(即没有感染源),返回-1. int days = 0; while (healthyCount > 0 && !queue.empty()) { int levelSize = queue.size(); for (int i = 0; i < levelSize; ++i) { auto [x,y] = queue.front(); queue.pop(); for 四个方向: if 在矩阵范围内且未感染: mark as infected queue.push(new坐标) healthyCount-- if (healthyCount ==0) { // 需要立即返回days+1? // 因为当前处理的是上一天的扩散,例如: // 当前处理的是第n天的节点,扩散后健康计数归零,所以总天数是n+1? // 需要看代码结构。 } } days++; } return healthyCount ==0 ? days : -1; 但是,这样处理的话,在示例1中,初始队列处理完后,days变为1。然后处理下一层,此时days变为2。在循环条件中,处理完第一层后,healthyCount=1,继续循环。处理第二层时,扩散到中间点,healthyCount变为0。此时,循环条件healthyCount>0不再满足,退出循环,返回days=2。 这似乎正确。因此,代码结构应该是: 在每次处理完一层后,days增加。处理完初始层后,days是1。然后处理下一层,days是2。此时,在循环条件中,healthyCount已经为0,退出循环,返回days=2. 因此,这样的代码结构是正确的。 现在,综合以上析,编写C++代码: 需要包含必要的头文件,如<vector>, <queue>, <sstream>等。 步骤: 1. 解析输入字符串为二维矩阵。 假设输入字符串如"1,0,1,0,0,0,1,0,1",使用istringstream割字符串,存入vector<int>,然后转换为n*n的矩阵。n = sqrt(vector.size())。例如,size=9 →n=3. 2. 初始化队列,并将初始感染点加入队列。同时计算健康区域的数量。 3. 检查初始条件:如果健康区域数量为0,或者初始队列为空(即无感染源),返回-1. 4. 进行BFS,每处理一层,days增加1. 5. 处理过程中,每次扩散到四个方向,如果该方向是健康的,则感染它,加入队列,并减少健康计数。 6. 循环结束后,如果健康计数为0,返回days,否则返回-1. 现在,编写代码: #include <iostream> #include <vector> #include <queue> #include <sstream> #include <string> #include <cmath> using namespace std; struct Position { int x; int y; Position(int _x, int _y) : x(_x), y(_y) {} }; int main() { string input; getline(cin, input); vector<int> cells; stringstream ss(input); string token; while (getline(ss, token, ',')) { cells.push_back(stoi(token)); } int n = sqrt(cells.size()); if (n * n != cells.size()) { cout << -1 << endl; return 0; } vector<vector<int>> matrix(n, vector<int>(n)); queue<Position> q; int healthy = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { matrix[i][j] = cells[i * n + j]; if (matrix[i][j] == 1) { q.push(Position(i, j)); } else { healthy++; } } } if (healthy == 0 || q.empty()) { cout << -1 << endl; return 0; } int days = 0; vector<pair<int, int>> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; while (healthy > 0 && !q.empty()) { int levelSize = q.size(); bool infected = false; for (int i = 0; i < levelSize; ++i) { Position curr = q.front(); q.pop(); for (auto& dir : dirs) { int x = curr.x + dir.first; int y = curr.y + dir.second; if (x >= 0 && x < n && y >= 0 && y < n && matrix[x][y] == 0) { matrix[x][y] = 1; q.push(Position(x, y)); healthy--; infected = true; } } } if (infected) { days++; } } if (healthy == 0) { cout << days << endl; } else { cout << -1 << endl; } return 0; } 然而,这段代码可能存在错误。例如,在BFS循环中,每次处理一层时,可能同时感染多个区域,但days的增加方式是否正确? 例如,在示例1中,初始队列中有4个节点。处理这一层时,每个节点扩散到四个方向,但只有部方向是有效的。例如,第一个节点(0,0)会感染(0,1)(1,0)。这两个新节点会被加入队列。处理完这四个节点后,层的大小是4,循环处理四次。每次处理一个节点,扩散到周围。如果有新感染,infected设为true。 处理完这一层后,如果infected为true,days加一。初始时,infected为true,所以days变为1。然后进入下一轮循环,处理队列中的四个新节点。每个节点扩散到周围,例如(0,1)感染(1,1),此时healthy减少到0。处理完这四个节点后,infected为true,days变为2。此时循环条件healthy>0不再满足,退出循环,返回days=2。正确。 另一个测用例,如果输入全0,则初始队列为空,返回-1. 如果输入全1,healthy初始为0,返回-1. 如果输入中无法全部感染,例如: 0 0 0 0 1 0 0 0 0 初始健康区域是8。初始队列中有1个节点(中间点)。处理这一层时,扩散到四个方向,健康减少4,变为4。days变为1。下一轮处理这四个新节点,每个节点扩散到周围,但可能中间已经被感染。例如,这四个新节点是上下左右。处理这四个节点时,每个节点扩散到周围,比如上节点(1,0)会扩散(0,0)(2,0),以及左右。处理完后,健康可能减少到0。假设矩阵是3x3,初始中间点感染,第一天扩散到上下左右四个点,第二天扩散到四个角落,此时健康计数为0,days是2. 因此,代码似乎能正确处理这些情况。 但原代码中的days计算可能有问题。例如,在循环中,每处理完一层,如果infected为true,则days++。但可能同一层中部节点导致感染,而其他节点没有。此时,days是否应该增加? 例如,如果同一层中有多个节点,其中某些导致感染,某些没有,那么处理完该层后,应该增加days一次,因为这一天有感染发生。 原代码中的逻辑是,如果该层有任何感染,则days增加。例如,在处理完一层后,如果有感染发生,则days++。这可能不正确,因为即使同一层中有多个感染,它们都属于同一天的扩散。 正确的做法是,每处理完一层(即一天的所有可能扩散),无论是否有新感染,days都增加。因为处理完一层代表一天过去。例如,初始队列中的节点属于第0天,处理它们后,days变为1,表示第一天扩散。如果在处理这一层时没有任何新感染,则后续循环结束,但此时healthy可能仍大于0,返回-1. 例如,假设初始队列中有感染点,但这些点无法扩散到任何地方(比如矩阵全是1,除了一个被包围的0),那么处理完初始队列后,进入循环,处理该层,此时无法扩散,infected为false,days不会增加。循环条件healthy>0且队列不空,队列此时可能还有后续的节点吗?可能没有,因为初始处理完后,队列被清空,下一层处理时队列为空,循环结束,返回-1. 因此,原代码中的逻辑可能有问题,因为在处理完一层后,只有当有感染发生时,才增加days。这可能导致天数计算错误。 正确的做法是,无论该层是否有新感染,只要处理了该层,就增加days。因为该层的处理代表了一天的过去。例如,某天可能没有新感染,但队列中仍有节点(可能处理的是之前的扩散节点)。 或者,正确的BFS方式应该是,每处理完一层,就代表过了一天。例如,初始队列中的节点是第0天,处理它们后,产生第1天的扩散。因此,无论是否有新感染,处理完一层后,days应该增加。 例如,修改代码中的循环部: int days = 0; while (healthy > 0 && !q.empty()) { int levelSize = q.size(); if (levelSize == 0) break; for (int i = 0; i < levelSize; ++i) { // 处理节点 // 扩散 } days++; } return healthy ==0 ? days : -1; 这样,无论是否有新感染,只要处理了一层,days就增加。但这样可能导致错误,例如,处理完初始队列后,days变为1,即使没有新感染。 例如,如果初始队列中的节点无法扩散(比如周围都是1),则处理完该层后,days变为1,但健康计数未改变,循环结束,返回-1. 这显然错误,因为实际没有发生任何扩散,所以天数应该是0。 因此,正确的处理方式应该是,days的初始值为0,并在每次处理完一层后,如果有新感染,则days加一。或者,days应该代表扩散所需的天数,即处理完一层后,如果有新感染,则天数加一。 这似乎回到最初的问题,如何正确计算天数。 可能更好的方式是,将days初始化为0,并在每次处理完一层后,如果有新感染发生,则days加一。这需要跟踪是否在该层中发生了感染。 例如: 在每次处理一层之前,先记录当前的健康计数。处理完该层后,如果健康计数减少了,则天数加一。 或者,在代码中,每次处理一层时,统计是否感染了任何新区域,如果有,则days加一。 例如: int days = 0; while (healthy > 0 && !q.empty()) { int levelSize = q.size(); int infectedThisRound = 0; for (int i = 0; i < levelSize; ++i) { auto curr = q.front(); q.pop(); for 每个方向: if 可以感染: ... infectedThisRound++; healthy--; } if (infectedThisRound > 0) { days++; } else { // 无法继续扩散,跳出循环 break; } } 这样,在示例1中,初始层处理感染4个区域,days变为1。下一层处理感染1个区域,days变为2. 这可能更准确。但如何跟踪infectedThisRound? 或者,可以计算在每层处理中,感染了多少区域,并根据此增加天数。 但这样可能复杂。另一种方法是,days变量在每次处理完一层后自动增加,但只有在健康计数减少的情况下才有效。例如,在示例1中,初始队列处理完后,即使没有新感染,days也增加,但这与实际情况不符。 这似乎很难处理。回到华为OD的示例,比如示例1的输出是2,所以正确的处理方式应该是,在处理完两层后,days=2. 因此,正确的代码可能应该将days初始化为0,并在每次处理完一层后,days++,无论该层是否有新感染。然后,在循环结束后,如果健康计数为0,返回days的值。但这样可能导致days包括没有发生感染的天数。 例如,假设某个层处理中没有新感染,但队列不为空(可能吗?)例如,初始队列处理完后,扩散到新节点,但处理这些新节点时无法进一步扩散。此时,处理完该层后,days增加,但健康计数未改变。循环继续,处理下一层(队列为空?),循环结束,返回-1. 因此,正确的处理方式可能需要确保,只有在有感染发生时,才增加days。否则,无法进一步扩散,应跳出循环。 因此,回到最初的代码,使用一个标志位来检测该层是否发生了感染: int days = 0; while (healthy > 0 && !q.empty()) { int levelSize = q.size(); bool infected = false; for (int i = 0; i < levelSize; ++i) { // process current node for 每个方向: if 可以感染: ... infected = true; healthy--; } if (infected) { days++; } else { // 无法继续感染,跳出循环 break; } } return healthy == 0 ? days : -1; 这样,在示例1中: 初始队列处理完后,infected=true,days=1. 处理下一层,infected=true,days=2. 健康计数为0,返回2. 在无法扩散的情况下,例如,队列中还有节点但无法扩散,infected=false,跳出循环,返回-1. 这种逻辑可能正确。 因此,修改代码中的循环部: int days = 0; vector<pair<int, int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}}; while (healthy > 0 && !q.empty()) { int levelSize = q.size(); bool infected = false; for (int i = 0; i < levelSize; ++i) { Position curr = q.front(); q.pop(); for (auto& dir : dirs) { int x = curr.x + dir.first; int y = curr.y + dir.second; if (x >=0 && x < n && y >=0 && y < n && matrix[x][y] == 0) { matrix[x][y] = 1; q.push(Position(x, y)); healthy--; infected = true; } } } if (infected) { days++; } else { break; } } if (healthy == 0) { cout << days << endl; } else { cout << -1 << endl; } 这样,在示例1中,处理初始队列后,infected=true,days=1,健康计数=1。处理下一层时,感染中间点,infected=true,days=2,健康计数=0。返回2,正确。 在另一种情况下,如无法扩散矩阵: 0 1 0 1 0 1 0 1 0 初始感染点是四个边中间的1,健康区域是中间的0。处理初始队列中的四个节点,每个节点尝扩散到周围。中间的0被四个节点中的每一个尝感染,但第一次感染时就会被标记,后续节点的扩散不会重复处理。例如,当处理第一个节点时,感染中间点,健康计数减为0,infected=true。处理完该层后,days=1。然后检查健康计数为0,返回1天。正确,因为中间点在第1天被感染。 综上,这段代码应该能正确处理各种情况。</think>以下是C++实现的疫情传播模拟代码,使用广度优先搜索算法模拟病毒扩散过程: ```cpp #include <iostream> #include <vector> #include <queue> #include <sstream> #include <cmath> using namespace std; struct Position { int x, y; Position(int _x, int _y) : x(_x), y(_y) {} }; int main() { string input; getline(cin, input); vector<int> cells; stringstream ss(input); string token; // 解析输入字符串 while (getline(ss, token, ',')) { cells.push_back(stoi(token)); } // 构建n*n矩阵 int n = sqrt(cells.size()); if (n * n != cells.size()) { cout << -1 << endl; return 0; } vector<vector<int>> matrix(n, vector<int>(n)); queue<Position> q; int healthy = 0; // 初始化队列和健康区域计数 for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { matrix[i][j] = cells[i * n + j]; if (matrix[i][j] == 1) { q.push(Position(i, j)); } else { healthy++; } } } // 处理特殊情况 if (healthy == 0 || q.empty()) { cout << -1 << endl; return 0; } int days = 0; vector<pair<int, int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}}; // BFS模拟扩散过程 while (healthy > 0 && !q.empty()) { int levelSize = q.size(); bool infected = false; for (int i = 0; i < levelSize; ++i) { Position curr = q.front(); q.pop(); // 向四个方向扩散 for (auto& dir : dirs) { int x = curr.x + dir.first; int y = curr.y + dir.second; if (x >= 0 && x < n && y >= 0 && y < n && matrix[x][y] == 0) { matrix[x][y] = 1; q.push(Position(x, y)); healthy--; infected = true; } } } if (infected) days++; else break; } cout << (healthy == 0 ? days : -1) << endl; return 0; } ``` ### 实现说明 1. **输入处理**:将逗号隔的字符串转换为$n \times n$矩阵,例如输入"1,0,1,0,0,0,1,0,1"转换为: $$ \begin{bmatrix}1&0&1\\0&0&0\\1&0&1\end{bmatrix} $$ 2. **初始化检查**:统计健康区域数量,若初始无感染源或全部感染则直接返回-1[^3][^5] 3. **BFS扩散**: - 使用队列存储感染区域坐标 - 每天向四个方向扩散,新感染区域入队 - 通过`healthy`计数器判断终止条件 4. **时间复杂度**:$O(n^2)$,每个区域最多被访问一次 ### 示例测 输入:`1,0,1,0,0,0,1,0,1` 输出:`2` 过程说明: 1. 第0天:4个初始感染点 2. 第1天:感染四周边缘区域 3. 第2天:感染中心区域[^3][^4]
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值