状态压缩 DP 入门课堂笔记

状态压缩型动态规划是一种实用的算法。顾名思义,它的优点就在于能够将一些可以表示为非 0 即 1 的状态进行压缩,从而大大节省了空间。说白了,状压 DP 就是利用了“位”的关系。因此位运算和集合的一些相关内容是学习状压 DP 必不可少的预备知识。

先来说集合。集合其实可以简单地看作一些数无序地集在一起,而且一般情况下集合中没有重复的元素。
举个例子吧。我们现在有集合 S={1,2,3,5,6}
那么就有一个“子集”的概念。子集是指选取原集合的某一些元素所组成的一个新集合,例如 {1,6,5} 就是 S 的一个子集,同样 {1,2,3,5,6} 自己也属于自己的子集。而 {4,3} 就不是,因为 S 里面没有 4。

在状态压缩型动态规划中,状态往往是可以表示成一个集合的。选了一些元素,其他的一些没选。而转移的时候,正是由子集转移到原集,因此,如何恰当地表示集合、枚举子集就成了关键。
当然,我们可以用数组来描述集合;但是这样一来,一些基本操作的时间都是线性的,常数已经很大了,对我们不利。

仔细想想,其实选或不选不就是0或1的两种情况么。不就可以表示成 n 位的二进制吗?!

例如 {1,2,4,5,6} ,我们可以表示为111011(高位在前!),对应的10进制就是59。
也就是说,有一个数 S ,虽然储存的时候是十进制,但是 C++ 已经有一些基本的自带位运算,例如并、或、异或、取反等,这些其实都可以对应集合的运算。

这里我们先讲讲子集判断的问题。现在我们有用数表示的集合 x 和集合 y ,如何在 O(1) 时间内判断 y 是不是 x 的子集呢?有两种思路,都是可行的。

一,根据子集的定义, y 中的每个元素都会在 x 中出现,也就是说,在对应位置上,如果 y 是 1,那么 x 也必须是1。那么就有

xy=y

二,既然子集属于原集的一部分,不应该有多出来的元素,也就是说,在对应位置上,如果 x 是 0,那么 y 也必须是0。那么就有
xy=x

要注意一个细节,在 C++ 中位运算符的优先级是要比大于号、小于号、等于号、大于等于、小于等于这些低的,所以判断的时候必须加括号。

原理并不难理解,但状态压缩型动态规划也是属于动态规划的范畴,最重要的还是找出子问题。

例题:原子弹

另外,int 最多表示32位,long long 最多表示64位,其实 C++ 的 STL 中还有一个 bitset,可以支持比较大的二进制集合运算。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值