状压dp概念和使用技巧

状压dp概念和使用技巧

1.概念

状压就是用一个N进制整数,表示一个状态。

比如:

1:有一个序列[A,B,C,D],序列中的每个元素可以拿或者不拿。
则一共有2^4种方式,可以用一个4位的2进制整数来表示每一种方式(状态)。
整数6[0110]表示拿了B,C
整数1[0001]表示拿了A
整数15[1111]表示都拿了

2:有一些人[a,b,c,d],每个人可以拿0,1,2个苹果。
则一共有3^4种方式,可以用一个4位的3进制数来表示每一种方式(状态)。
整数3表示的3进制数为[0010],表示只有b拿了一个苹果。
整数5表示的3进制数为[0012],表示b拿了一个苹果,a拿了2个苹果。

2.状态表示和计算

在使用2进制表示状态的时候,因为计算机底层表示一个整数就用的2进制,所以很容易通过位运算得知每一位的数是多少。

但是如果使用N进制表示状态,并且N不是2,4,8…,比如3进制,5进制;此时要计算该整数每一位的数是多少就不能使用位运算了。

我们知道一个n位的m进制数,表示的十进制整数S为
S = x n − 1 ∗ m n − 1 + x n − 2 ∗ m n − 2 + . . . + x 0 ∗ m 0 S = x_{n-1}*m^{n-1} + x_{n-2}*m^{n-2}+...+x_0*m^{0} S=xn1mn1+xn2mn2+...+x0m0

因此想要计算的整数Sm进制表示中的第i位的数是多少,可以使用下面的公式:
x i = ( S % m i ) / m i − 1 x_i = (S\%m^i)/m^{i-1} xi=(S%mi)/mi1

比如上面的示例2中的整数5:
x0 = (5%3)/1 = 2
x1 = (5%9)/3 = 1

3.使用技巧

1.一般可以使用回溯法求解的问题,并且状态不多时,就可以考虑使用状压dp;对于某些图问题,通常会通过按行或者按列进行dp,每一行或每一列的选取状态用一个整数表示。

2.排列/组和问题,在状态比较少的时候也可以使用状压dp。

状压dp的好处就是在使用回溯法,dfs,bfs求解时,遍历所有的状态需要的时间复杂度是O(n!)级别的,但是状压dp可以将复杂度降为O(m^n),m为表示状态的使用的进制。

4.实现方式

1.自底向上

自底向上根据情况又有2种实现方式,【条件1】:自底向上要求在遍历到状态S时,所有能转移到它的状态的dp值已经计算完成

(1).状态转移方程明确,对于一个状态S所有可以转移到S状态的状态集合c明确,并且容易获取。

​ 就可以使用传统dp的实现方式,循环遍历每一个状态S,根据状态S的所有能转移到它的状态集合C,计算出该状态S的dp值。

​ 这样就可以计算出所有状态的dp值了。

(2).不符合第(1)种情况。

​ 如果不好找到上述状态S的集合c,或者找到该集合c需要的代价很大。

​ 则可以遍历每一个有效状态S,然后根据S计算出所有它能转移到的状态,更新这些状态的dp值。

第(2)种的实现中,假如共有state种状态,在遍历到一个状态S时,要保证能转移到S的状态的dp值已经计算完成。
则
a). 可以通过for(int i = 0;i<state;i++)来遍历每一个有效状态,然后更新所有它可以转移到的状态的dp值;适用于状态只会由小转大(大部分情况都是这样),这种情况,此方法就可以满足条件1.
b). 可以通过队列来实现,类似于bfs;但是也得注意是否满足条件1。

2.自顶向下带备忘录DFS

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值