什么是状态压缩DP???

1. 引言

相信大家已经对普通的01背包或者其他背包问题已经很熟练了,但是有时候我们去解决NP问题(指数级别的复杂度,比如N!),时间复杂度就会非常之大

所以,这个时候我们需要寻找更加优化的方法,来优化DP

状态压缩也是一种DP优化方法

2. 什么是状态压缩?

状态压缩:是一种优化动态规划算法的技术,通常用于解决具有大量状态的问题,以减少内存消耗和提高计算效率。在动态规划问题中,状态通常是指描述问题的各种情况或者组合的变量,而状态压缩就是通过一种巧妙的方式来表示和存储状态,从而减少内存消耗。

也就是说,当DP状态是集合的时候,把集合的组合或者排列用二进制进行表示,这个二进制01组合表示集合的的一个子集 , 可以将DP状态的处理转成为二进制的位操作,能够提高算法的效率

当一个DP具有以下特征的时候,就适合用状态压缩了:

  1. 状态之间存在某种转移关系,但状态数量巨大。
  2. 状态之间的转移可以通过位运算来表示。
  3. 状态之间的转移关系可以通过枚举子集或者排列组合的方式来计算。

3. 引子

现在,我们来看一道经典问题,一起学习什么是状态压缩。

给定一张 n个点的带权无向图,点从 0∼n−1标号,求起点 0到终点 n−1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0到 n−1不重不漏地经过每个点恰好一次。

输入格式
第一行输入整数 n。

接下来 n行每行 n个整数,其中第 i行第 j个整数表示点 i到 j的距离(记为 a[i,j])。

对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]。

输出格式
输出一个整数,表示最短 Hamilton 路径的长度。

数据范围
1≤n≤20

0≤a[i,j]≤107

输入样例:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0


输出样例:
1

Hamilton(旅行商)是一个NP问题,N个点的全排列,时间复杂度是n!这是非常之大的。

有人看到最短路或许会无脑dijkstra, 但是对于dijkstra求最短路是并没有考虑每个点的

这道题的难度就在每个点都需要访问一次,所以并不能适用

就算使用回溯搜索,那也非常大的时间复杂度

4. DP求解Hamilton问题

4.1 状态定义

如何进行DP状态定义,这是我们首要解决的

DP问题,无非就是从小问题递推到大问题,那么我们可以这样进行dp定义:

dp[s][j]:代表在S集合内,到达j点的最短距离(j也在S集合内,因为每个点都需要到达)

我们可以通过子集然后递推求出大集合

4.2 属性定义

这道题很明确,其实就是求最小值,也就是min

4.3 状态转移

从集合S到j点的最短距离,无非就是需要从前面的集合开始入手

我们可以从小问题S-j递推到大问题S

S-j:代表从集合S中去除j点的集合

如何从S-j递推到S又是一个难题,我们可以设k为S-j中的一个点,把0~j 分为两个部分:

第一部分就是:0->k , 第二部分是k->j

我们先到k点,然后从k点到j点,于是就有了以下状态转移方程式

dp[S][j] = min(dp[S - j][k] + dist[k][j])

dist[k][j]:k到j的距离

这个地方会比较难以理解,但是要清楚:DP就是从小问题递推到大问题

我们要求S集合内到达j点的最短路,无非就是要从S中把j点去除,然后通过中间的那些点作为媒介,一个一个递推到S -> j

 5. 代码

知道这些,我们可以开始编码了:

解析:

  • 状态压缩,是将一个二进制数表示一个集合S,二进制的每一位都代表图上的每一个点。
  • 对于二进制位每一位来说,有1就代表全都有,比如n = 5时,二进制为:11111 , 也就代表这个集合里面有1、2、3、4、5几个点
from math import *

n = int(input())

dist = [[0] * (n + 1) for i in range(n + 1)]    # 两个点之间的距离
for i in range(n):
    a = list(map(int, input().split()))
    for j in range(len(a)):
        dist[i][j] = a[j]

# 集合S到j的最短距离 , 这里的1 << 20是因为题目的范围是1 - 20 , 最大只有20个点,通过位运算的形式,将这21个点转为集合
dp = [[inf] * (n + 1) for i in range(1 << 20)]   # 初始化都是最大的


dp[1][0] = 0  # 最开始,集合里面只有一个0点
for s in range(1 << n):    # 二进制总共的位数
    for j in range(n):
        if (s >> j) & 1:   # 判断j是否在S中
            for k in range(n):
                if (s ^ (1 << j)) >> k & 1:   # 判断k是否在S除去j里面

                    dp[s][j] = min(dp[s][j], dp[s ^ (1 << j)][k] + dist[k][j])  # 状态转移方程

print(dp[(1 << n) - 1][n - 1]) 

相关推荐

  1. 算法:状态压缩dp

    2024-03-21 00:24:03       112 阅读
  2. 什么模型压缩技术

    2024-03-21 00:24:03       101 阅读
  3. 什么http状态码?

    2024-03-21 00:24:03       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-21 00:24:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-21 00:24:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-21 00:24:03       82 阅读
  4. Python语言-面向对象

    2024-03-21 00:24:03       91 阅读

热门阅读

  1. 智慧能源-数字化能源转型革命

    2024-03-21 00:24:03       42 阅读
  2. tcp拥塞控制详解

    2024-03-21 00:24:03       37 阅读
  3. C语言学习笔记day10

    2024-03-21 00:24:03       41 阅读
  4. 在AI中无所不在的微积分

    2024-03-21 00:24:03       41 阅读
  5. 如何防御XSS攻击

    2024-03-21 00:24:03       38 阅读
  6. LeetCode1492. The kth Factor of n

    2024-03-21 00:24:03       44 阅读
  7. 如何在 Flutter 中实现地理定位和地图功能?

    2024-03-21 00:24:03       39 阅读
  8. Linux命令-dhclient命令(动态获取或释放IP地址)

    2024-03-21 00:24:03       46 阅读
  9. 一篇文章搞懂vue基础(上)

    2024-03-21 00:24:03       35 阅读
  10. stm32F407+ESP8266+AT指令+阿里云+代码进阶版(4)

    2024-03-21 00:24:03       36 阅读
  11. ARM汇编程序设计 注释 “每日读书“

    2024-03-21 00:24:03       41 阅读
  12. 彻底讲透:mysql mvcc原理

    2024-03-21 00:24:03       43 阅读
  13. 数据结构-哈希表(二)

    2024-03-21 00:24:03       43 阅读