背景
记录2023-10-21 晚华为OD三面的手撕代码题,当时没做出来,给面试官说了我的想法,评价:解法复杂了,只是简单的动态规范 或 广度优先算法,事后找资料记录实现方式。
题目
腐烂的橘子
问题描述:
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
【每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。】
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。【如果不可能,返回 -1。】
示例 1:
输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:[[2,1,1],[0,1,1],[1 ,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
实现一(广度优先)
求解思路
就按照题目描述,
(1)按照时间 线
(2)逐步 遍历所有的位置
(3)根据规则,如果有坏橘子,且四个正方向(东南西北)有好橘子,则更新之
(4)设计一个状态变量,记录当前时刻:是否有橘子腐败,如果无橘子腐败,是 全部好,还是全部坏,亦或者 好坏相间。
代码实现
testA = [[2,1,1],[1,1,0],[0,1,1]]
testB = [[2,1,1],[0,1,1],[1,0,1]]
testC = [[0,2]]
M_position = testA
print(M_position)
row = len(M_position)
col = len(M_position[0])
set_bad = []
for ir in range(row):
for jc in range(col):
if M_position[ir][jc]==1:
set_bad.append((ir, jc))
print(row, col)
# 一次时间变更
def onetimeupndate():
# 状态变量
# 0 无橘子
# 1、全部新鲜
# (1, 2) 好坏 混杂,且未 感染
# 2、全部坏、
# 3、坏感染
flag = 0
# 变更状态的坐标
changed_set = []
for ir in range(row):
for jc in range(col):
if M_position[ir][jc]==0:
continue
# 记录唯一状态
if flag!=3:
flag = (flag + M_position[ir][jc])/(1 if flag==0 else 2)
# 坏橘子才更新
if M_position[ir][jc]!=2 or ((ir, jc) in changed_set):
continue
# 遍历方向
for i,j in [[1,0],[0,1],[-1,0],[0,-1]]:
ar_, bc_ = ir + i, jc + j
if ar_<0 or ar_>=row or bc_<0 or bc_>=col:
continue
if M_position[ar_][bc_]==1:
# 记录更新的坐标
changed_set.append((ar_, bc_))
# 标记状态
flag = 3
# 换橘子
M_position[ar_][bc_]=2
return flag
itime = 0
while True:
flag = onetimeupndate()
print("当前次数",itime)
print("状态变量",flag)
print("最新结果",M_position)
if flag!=3:
if 1<flag<2:
itime = -1
break
itime +=1
print("结果",itime)
实现二(动态规范)
求解思路
- 问题抽象为 求每个点 到最近的 坏的橘子的 最短距离
- 构造初始距离矩阵,约定:
0、坏橘子 inf、永远不坏 row*col、好橘子最大距离
- 做两次 扫描,更新最短距离
- 得到整体最短距离,找到 永不更新的点
代码实现
def calmintime(M_position):
# 最短路径矩阵
M_status = []
# 尺寸信息
row = len(M_position)
col = len(M_position[0])
MAXTIME = row * col
# 初始矩阵
for ir in range(row):
for ic in range(col):
if ic == 0:
M_status.append([])
if M_position[ir][ic] == 2: # 腐烂
v = 0
elif M_position[ir][ic] == 0: # 无
v = inf
elif M_position[ir][ic] == 1: # 新鲜
v = MAXTIME # 设为一个到不了的实数最大值
M_status[ir].append(v)
# 往左下遍历,比较右上两个方向
for ir in range(row):
for ic in range(col):
if M_position[ir][ic]==0:
continue
if ic-1>=0:
if M_status[ir][ic-1]+1<M_status[ir][ic]:
M_status[ir][ic] = M_status[ir][ic-1]+1
if ir-1>=0:
if M_status[ir-1][ic]+1<M_status[ir][ic]:
M_status[ir][ic] = M_status[ir-1][ic]+1
# 往 右上遍历,比较左下两个方向
for ir in reversed(range(row)):
for ic in reversed(range(col)):
if M_position[ir][ic]==0:
continue
if ic+1<col:
if M_status[ir][ic+1]+1<M_status[ir][ic]:
M_status[ir][ic] = M_status[ir][ic+1]+1
if ir+1<row:
if M_status[ir+1][ic]+1<M_status[ir][ic]:
M_status[ir][ic] = M_status[ir+1][ic]+1
# 最短距离
vlist = []
for ir in range(row):
for ic in range(col):
v = M_status[ir][ic]
if not v is inf:
vlist.append(v)
if len(vlist)==0:
return 0
vmax = max(vlist)
if vmax==MAXTIME: # 还存在新鲜的橘子
return -1
else:
return vmax
testA = [[2,1,1],[1,1,0],[0,1,1]]
testB = [[2,1,1],[0,1,1],[1,0,1]]
testC = [[0,2]]
结果验证
参考资料
矩阵(广度优先搜索)(动态规划)