<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]