题目重点:逐日状态转移需要固定传播阈值、免疫时点和终止目标。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 格子为健康.、感染X、永久免疫I;八邻居,健康格子至少threshold个感染邻居才感染,threshold为1..8。每天同步更新,今天新感染者明天才传播。
- 初始X的感染日为0,新感染者记实际日。感染D天后永久变I,训练默认先恢复再传播,也支持相反顺序供对照;初始X同样需要恢复。
- 代码返回最后一次状态改变的日数与最终格局,不保证所有格子感染。要求“全感染”或“无健康”时应另外检查状态并报告不可达。max_days是运行预算,超过时报错,不当成最终结果。
解题路线
- 先做无恢复版本:每日从同一旧状态算所有new,最后统一写回;原地看到X立即扩散会让一天穿过任意长链。免疫格永不加入new。
- 恢复版维护born[cell]=感染日,每天找day−born≥D的due。如果先恢复,due不参与今天邻居计数;如果先传播,due今天仍可传染,随后变I。两种合同在D=1时会产生完全不同结果。
- 没有新感染并不代表已平衡:尚未到期的X将来还要恢复,而且threshold>1可能使传播长期受阻。需继续到没有感染者;无恢复时当某日没有new即可结束。对大D可跳到下一到期事件,但跳跃期间若还有可传播healthy必须逐日或使用证明充分的事件推进。
- 阈值1且无恢复可用多源BFS,免疫视障碍;threshold>1需要邻居计数与同步下一层,不能只取最近源距离。恢复进一步使已累计邻居可能减少,普通“达到阈值永久入队”必须保持日内次序。
手动走一遍
一行X..、threshold1,无恢复:day1 XX.,day2 XXX,最后改变日2。同一输入D1,先恢复得到I..、最后日1;先传播依次IX.、IIX、III,最后日3。初始X若漏计到期会错误继续传播。XI.中间I阻断一维传播,最终保留健康格;“平衡态”可以有.。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def simulate(grid,threshold=1,recovery=None,recover_first=True,max_days=10000):
if not grid or not grid[0] or any(len(row)!=len(grid[0]) for row in grid):raise ValueError("nonempty rectangle")
if threshold<1 or threshold>8 or (recovery is not None and recovery<1):raise ValueError("threshold1..8/recovery positive")
state=[list(row) for row in grid]
if any(x not in '.XI' for row in state for x in row):raise ValueError("plant state")
rows,cols=len(state),len(state[0]);born={(r,c):0 for r in range(rows) for c in range(cols) if state[r][c]=='X'}
for day in range(1,max_days+1):
due={cell for cell,at in born.items() if recovery is not None and day-at>=recovery}
active={cell for cell in born if not (recover_first and cell in due)}
new=[]
for r in range(rows):
for c in range(cols):
if state[r][c]!='.':continue
neighbors=sum((r+dr,c+dc) in active for dr in (-1,0,1) for dc in (-1,0,1) if dr or dc)
if neighbors>=threshold:new.append((r,c))
changed=bool(due or new)
for r,c in due:state[r][c]='I';del born[(r,c)]
for r,c in new:state[r][c]='X';born[(r,c)]=day
if not changed and (recovery is None or not born):return day-1,[''.join(row) for row in state]
if recovery is not None and not born:return day,[''.join(row) for row in state]
raise RuntimeError("simulation day budget exceeded")
复杂度
R×C格、T模拟日,基线O(TRC),状态/感染日O(RC),邻居数固定8。无恢复每格最多感染一次,阈值1多源BFS可O(RC);恢复不能据此把逐日扫描直接宣称线性。
易错点与追问
容易失分的边界
- 恢复日用day−born>D会多传染一天。
- 把“感染结束”误写成所有人感染,或没有new时提前跳过未来恢复。
- 第五阶段未明确规则时不能擅自添加再感染、随机概率或GPU模拟。
继续深挖
- 怎样用感染/到期事件及每格邻居计数减少大地图扫描?
- 如果恢复后重新易感,系统可能循环,终止与检测周期如何改变?
