状态压缩 DP
到这里,我们知道了 S 到所有 M 的最短距离,也知道了 M 互相之间的最短距离,也知道了 M 到 T 的最短距离,这就是一个经典的状态压缩 DP 的模型了。

复杂度分析

注意事项
- 由于本题的复杂度较高,使用 Python 等性能较差的语言实现时需要注意效率问题。
- 本题边界情况较多,比如迷宫没有 M 、M 不可达等。

Python 实现
import Queue
class Solution(object):
def minimalSteps(self, maze):
"""
:type maze: List[str]
:rtype: int
"""
n = len(maze)
m = len(maze[0])
p = []
#记录所有特殊点
for i in range(n):
for j in range(m):
if maze[i][j] in ['S','T','M','O']:
p.append((i,j,maze[i][j]))
dxs = [0,1,0,-1]
dys = [1,0,-1,0]
def bfs(x, y):
q = Queue.Queue()
q.put((x,y))
dis = [[10000 for i in range(m)] for j in range(n)]
dis[x][y] = 0
while not q.empty():
_ = q.get()
x = _[0]; y = _[1]
for dx, dy in zip(dxs, dys):
nx = x + dx; ny = y + dy
if nx < 0 or nx == n or ny < 0 or ny == m: continue
if maze[nx][ny] == '#': continue
if dis[nx][ny] > dis[x][y] + 1:
dis[nx][ny] = dis[x][y] + 1
q.put((nx,ny))
res = []
for i, j, _ in p:
res.append(dis[i][j])
return res
#计算特殊点之间的最短距离
tag = {}
dis = []
for idx, (i, j, t) in enumerate(p):
dis.append(bfs(i, j))
if not t in tag: tag[t] = []
tag[t].append(idx)
sidx = tag['S'][0]
tidx = tag['T'][0]
#特殊处理 M 不存在的情况
if not 'M' in tag:
ans = dis[sidx][tidx]
if ans == 10000:
ans = -1
return ans
#计算 S 到所有 M 点之间的最短距离
Mnum = len(tag['M'])
Onum = len(tag['O'])
dp = [[10000 for i in range(Mnum)] for i in range(1<<Mnum)]
for i in range(Mnum):
midx = tag['M'][i]
s = 1 << i
for j in range(Onum):
oidx = tag['O'][j]
dp[s][i] = min(dp[s][i], dis[sidx][oidx] + dis[oidx][midx])
# 预处理 M 点之间的最短距离
Mdis = [[10000 for i in range(Mnum)] for j in range(Mnum)]
for i in range(Mnum):
midx1= tag['M'][i]
for j in range(Mnum):
midx2 = tag['M'][j]
for k in range(Onum):
oidx = tag['O'][k]
Mdis[i][j] = min(Mdis[i][j], dis[midx1][oidx] + dis[oidx][midx2])
#状态压缩DP
for s in range(1<<Mnum):
for j in range(Mnum):
if s & (1<<j) == 0: continue
for k in range(Mnum):
if s & (1<<k) != 0: continue
ns = s | (1<<k)
dp[ns][k] = min(dp[ns][k], dp[s][j] + Mdis[j][k])
# 统计结果
ans = 10000
fs = (1<<Mnum) - 1
for j in range(Mnum):
midx = tag['M'][j]
ans = min(ans, dp[fs][j] + dis[midx][tidx])
if ans == 10000: ans = -1
return ans