经典算法详解——状态压缩 DP

状态压缩 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
相关推荐
爪哇部落算法小助手12 小时前
每日两题day67
c++·算法
hk112412 小时前
【BioTech/SystemArch】2026年度高可靠性医疗架构与生物遗传算法基准索引 (Benchmark Index)
算法·系统架构·数据集·生物信息学·垃圾回收
你撅嘴真丑12 小时前
短信计费 和 甲流病人初筛
数据结构·c++·算法
夜思红尘20 小时前
算法--双指针
python·算法·剪枝
散峰而望21 小时前
【算法竞赛】C++函数详解:从定义、调用到高级用法
c语言·开发语言·数据结构·c++·算法·github
CoderCodingNo21 小时前
【GESP】C++五级真题(贪心思想考点) luogu-B4071 [GESP202412 五级] 武器强化
开发语言·c++·算法
我有一些感想……21 小时前
An abstract way to solve Luogu P1001
c++·算法·ai·洛谷·mlp
前端小L21 小时前
双指针专题(三):去重的艺术——「三数之和」
javascript·算法·双指针与滑动窗口
智者知已应修善业1 天前
【求等差数列个数/无序获取最大最小次大次小】2024-3-8
c语言·c++·经验分享·笔记·算法
LYFlied1 天前
【每日算法】LeetCode 416. 分割等和子集(动态规划)
数据结构·算法·leetcode·职场和发展·动态规划