一、什么是状态压缩?
状态压缩,其实是把“很多信息”用“很少的空间”巧妙地记录下来,常见于动态规划(DP)等算法中,尤其是涉及“集合”或“多种状态”的问题。
二、形象比喻
比喻一:钥匙串上的钥匙
想象你有一串钥匙,每把钥匙代表一个房间是否被打开。
- 钥匙在钥匙串上,表示房间没开。
- 钥匙不在钥匙串上,表示房间已经开了。
如果你有10个房间,理论上你要用10个“是/否”来记录每个房间的状态。
但你很聪明,把每个房间编号(0~9),用一串二进制数来表示:
- 1011000101
1表示钥匙还在(房间没开),0表示钥匙不在(房间开了)。
这样,一串数字就能记录所有房间的开关状态,而不是用10个变量!
比喻二:打卡签到表
你和朋友去旅游,每到一个景点就打个卡。
- 有5个景点,每个景点打没打卡都要记录。
- 你可以用一个“打卡表”来记录:
比如 11001,表示第1、2、5个景点打过卡,3、4没打。
如果用一个整数的二进制位来表示,
- 11001(二进制)= 25(十进制)
这样,只用一个数字25,就能记录5个景点的打卡情况!
三、状态压缩的“魔法”
1. 用二进制位表示“有/无”、“开/关”、“选/不选”
- 每一位代表一个元素的状态。
- 0/1表示不同的状态。
2. 用一个整数就能表示一组状态
- 5个元素,只要5位二进制(最大31)。
- 20个元素,只要20位二进制(最大1048575)。
3. 检查和修改状态很方便
- 检查第i个元素是否选中:看第i位是0还是1。
- 选中/取消第i个元素:用位运算(|、&、^)一行代码搞定。
四、生活中的状态压缩
- 闹钟开关:你手机有7个闹钟,每个开关都可以用一位二进制表示,7个位就能记录所有闹钟的开关状态。
- 灯光控制:舞台上有10盏灯,每盏灯开关都可以用一位二进制表示,10个位就能记录所有灯的状态。
五、算法中的应用
- 旅行商问题(TSP):记录哪些城市已经走过,用二进制压缩成一个整数。
- 集合型DP:比如“选哪些物品”,“哪些点被访问过”等等。
六、形象总结
状态压缩就像用一串二进制灯泡,亮着的表示“选了”,灭着的表示“没选”,只用一个数字就能记录一大堆开关的状态,既省空间又方便操作!
七、状态压缩的“超级背包”
1. 普通背包
想象你有一个背包,要装很多东西,每样东西要么装要么不装。
如果你用一个数组来记录每种装法,数组会非常大,尤其是东西多的时候。
2. 状态压缩背包
你很聪明,把每种装法都用一个二进制数来表示:
- 0 1 0 1 1
表示第1、3个没装,第2、4、5个装了。
这样,所有可能的装法,都能用0~2^n-1这些数字来表示(n是物品个数)。
八、状态压缩的“魔法操作”
1. 检查某个物品有没有装
比如状态是 10101(二进制),你想知道第3个物品有没有装:
- 看第3位(从右往左数,0开始),是1就装了,0就没装。
- 代码里可以用:
(state >> 2) & 1
,如果结果是1,说明装了。
2. 装上/卸下某个物品
- 装上第i个物品:
state | (1 << i)
- 卸下第i个物品:
state & ~(1 << i)
3. 枚举所有状态
- 只要for循环从0到2^n-1,就能遍历所有可能的装法。
九、状态压缩的“超级记忆法”
1. 记忆化搜索
有时候你要记住“某种状态下的最优解”,比如TSP问题,
你可以用一个二维数组 dp[state][pos]
,
- state表示哪些城市已经走过(用二进制压缩)
- pos表示当前在哪个城市
这样,所有可能的“走法”都能被唯一地记录下来,既快又省空间。
十、生活中的再举例
1. 电视遥控器
你有一个遥控器,能控制10台电视的开关。
- 你可以用一个10位的二进制数,记录每台电视的开关状态。
- 只要一个数字,就能表示所有电视的组合状态。
2. 会议签到
公司有20个人,每个人是否到场都要记录。
- 用一个20位的二进制数,1表示到场,0表示没到。
- 只要一个数字,就能表示所有人的到场情况。
十一、状态压缩的“省空间大法”
如果你用数组来记录每个人的到场情况,要20个变量。
用状态压缩,只要一个数字(int类型),就能全部搞定!
十二、状态压缩的“高效查找”
- 你想查找“哪些人都到场了”?只要判断数字是不是111…1(二进制全1)。
- 你想查找“只差一个人没到”?只要判断数字里有且只有一个0。
十三、状态压缩的“代码小片段”
比如有4个物品,枚举所有装法:
n = 4
for state in range(1 << n): # 0~15
for i in range(n):
if (state >> i) & 1:
print(f"第{i+1}个物品已装")
else:
print(f"第{i+1}个物品未装")
十四、形象总结
状态压缩就像用一串灯泡来记录复杂的开关组合,只要一个数字就能表示成千上万种状态,既省空间又方便操作,是算法里的“超级记事本”!