zoukankan      html  css  js  c++  java
  • [算法复习]状压DP 更新中

    emmm突然想来讲讲状压DP,这次先简单讲讲状压DP是什么和枚举子集的方法吧。
    有时间的话会做点题整理一下放上来。

    状压DP

    状态压缩

    大概是这样,比如你有n个物品,可以取也可以不取,那么我们用1代表取了,0代表枚举,排成一列长度为n的数列,就可以代表n个物品的取舍状况。
    然后我们将这个数列视为一个二进制串,就得到了一个状态,比如11101。这就是状态压缩。
    一般来说对于一个状态(f[11101_{(2)}])(f[29_{(10)}]),我们可以从它的子集转移到它。
    所以我们下面来讨论如何枚举子集

    子集枚举

    来看一段伪代码

    for(s1 = s; s1 >= 1; s1 = (s1 - 1) & s)
    

    首先我们可以知道枚举出来的s1必然是s的子集,因为s是s的子集,一个数&s的结果肯定也是s的子集
    接下来我们考虑为什么s1可以枚举完所有s的子集(除了空子集)
    s1 - 1,相当于将s1中最右侧的1变成0,同时将其右侧全部变为1.&s相当于把右侧中不在s里的1都去掉。
    所以一次迭代相当于s1中最右侧的1去掉,同时还原这个1右侧的在s中的所有1,不影响被去掉的1的左侧的1
    那么我们考虑每次迭代s1必然是减小的,且最后一定会减小到0,同时每次操作最多只会删除1个已有的1,可能还原出一堆1.
    所以我们可以知道,对于s中的任意一个1,必然存在某一时刻,它被删除了,而它左边还没开始删,那么此时它右边的1都会被还原出来。
    也就是说这个时刻相当于s中删且只删了这个1.
    因为对于任意一个1都存在这个时刻,因此我们可以知道s只去除1个1的子集肯定是枚举完了。
    然后我们用递归的思想理解一下。既然我们对于s只去除一个1的子集都枚举完了,那么我们在枚举到这些只去除一个1的子集的时候,相当于将这个子集作为新的s往下枚举。
    那么我们又可以枚举完这个子集所有只去掉一个1的子集,也就相当于s只去掉两个1的子集……
    以此类推,我们可以知道最后我们可以枚举完s去掉任意个1的子集。

    常见的状态变换

    对s的第i位进行操作

    (k = 1 << (i - 1))即可得到一个第i位为1,其他位为0的二进制串,然后用这个二进制串和s进行操作即可。
    比如:
    判断第i位是否为1:s & k > 0 则第i位为1,否则为0
    将第i位改为1:s = s | k
    将第i位改为0:s = s & ~k (此时k的第i位为0,所以可以强制s的第i位为0,k的其他位为1,因此不会影响s的其他位)

    去掉s最右的1

    s = s & (s - 1)
    这里类似于前面的子串枚举了。
    s - 1得到去掉最靠右的1且该1的右侧全变为1的二进制串。
    就是上文中

    所以我们可以知道,对于s中的任意一个1,必然存在某一时刻,它被删除了,而它左边还没开始删,那么此时它右边的1都会被还原出来。
    也就是说这个时刻相当于s中删且只删了这个1.

    这个任意1为最右侧1的情况。

    本文不允许商业性使用,个人转载请注明出处! 知识共享许可协议
    本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。
  • 相关阅读:
    你了解JWT吗?
    链接
    C#读取EXCEL发生错误
    TM1637驱动数码管
    Keil中的Code,RO,RW,ZI分别表示什么
    IE 不支持 promise 解决方法
    JS 时间戳转日期格式
    JS input 输入框只能输入 字母和汉字
    小程序 保存图片失败
    小程序充值,方法步骤
  • 原文地址:https://www.cnblogs.com/ww3113306/p/14672645.html
Copyright © 2011-2022 走看看