状态压缩DP

状压DP 又叫集合动态规划。是以结合信息为状态的特殊的动态规划的问题。主要有传统集合动态规划基于连通性状态压缩的动态规划两种。

状压DP和一般的DP的区别

一般的DP:着眼于整体,从中提取出几个关键信息并以此划分阶段使得问题具备无后效性或最优子性质,然后根据已知的信息依次对各个阶段进行决策。 状压DP:如果一般的状态难以描述无后效性原则,或者信息不足无法决策,许多元素的状态都直接影响的决策,都需要被考虑到。所以给每个元素都开个数组来存是行不通的。于是状压DP应运而生,它可以对多个元素的状态进行压缩存储

状态压缩预备知识–位运算

位运算是一种速度非常快的基本运算。有很多种操作

算数左移(<<):左移一位,相当于该数乘以2x2^x2x 算数右移(>>):右移一位,相当于该数除以2x2^x2x
与运算(&):两数同一位都为111时结果为111,否则为000。或运算(|):两数同一位都为000时结果为000,否则为111。
非运算(~):按位取反。例如~101=010

二进制状态压缩,是指将一个长度为mmm的boolboolbool数组用一个mmm位222进制整数表示并存储的方法。利用下列位运算操作可以实现原boolboolbool数组中对应下标元素的存取。(注意,为了不让优先级混乱,所以建议所有长运算都加括号)

取出整数nnn在二进制表示下的第kkk位 (n>>k)&1(n>>k)~~\&~~1(n>>k)  &  1
取出整数nnn在二进制表示下的第000至k−1k-1k−1位 n&((1<<k)−1)n\&((1<<k)-1)n&((1<<k)−1)
把整数nnn在二进制表示下的第kkk位取反 nxor(1<<k)n ~ xor ~(1<<k)n xor (1<<k)
对整数nnn在二进制表示下的第kkk位赋值111 n∣(1<<k)n|(1<<k)n∣(1<<k)
对整数nnn在二进制表示下的第kkk位赋值000 n&(∼(1<<k))n~~\&~~(\sim(1<<k) )n  &  (∼(1<<k))

从TSP问题引入状压DP

例题: 最短Hamilton路径

给定一张nnn个点的带权无向图,点从 0∼n−10 \sim n−10∼n−1标号,求起点 000 到终点 n−1n−1n−1 的最短 Hamilton 路径。 Hamilton 路径的定义是从000到n−1n−1n−1不重不漏地经过每个点恰好一次。

分析:不重不漏的经过一个带权无向图,这就是著名的NP-Complete问题的一个经典问题–TSP。那么对于这样一类没有多项式算法的问题,搜索算法(时间复杂度为O(n!)O(n!)O(n!))并非是一个解决问题的唯一途径。

不难发现我们只需要知道**(1)哪个点被用过。(2)目前停在哪个点上。**DP问题崭露头角。我们同时发现任何时候我们只需要知道哪些点已经被遍历过而遍历点耳朵具体顺序对以后的决策是没有影响的。至此,我们的状态已经出现了。

设statestatestate为状态。则有f[state][j]=f[state_k][k]+w[k][j]f[state][j]=f[state\_k][k]+w[k][j]f[state][j]=f[state_k][k]+w[k][j],其中state_k=state是包含k且除掉j之后的集合state\_k=state是包含k且除掉j之后的集合state_k=state是包含k且除掉j之后的集合。我们用二进制来表示statestatestate,如果这个点存在则为111,不存在则为000。则statestatestate=(iii>>jjj)&1\&1&1,意为iii右移jjj位,通俗一点说就是将iii移到个位上再&1\&1&1,就可以取出iii的第jjj位。如果state=1state=1state=1则合法,反之不合法。我们以同样的思想就可以实现state_k=i−(1state\_k=i-(1state_k=i−(1<<j)j)j)>>k&1k\&1k&1,本题得到解决。

#include <bits/stdc++.h>
using namespace std;
long long n,f[1<<20][21],w[1000][1000];
int main(){memset(f,0x3f,sizeof(f));cin>>n;for(int i=0;i<n;i++)for(int j=0;j<n;j++) cin>>w[i][j];f[1][0]=0;//初始状态,第1个点被用过,目前停在第0个点上,等于0 for(int i=1;i<(1<<n);i++)for(int j=0;j<n;j++)if(i>>j&1)for(int k=0;k<n;k++)if(i>>k&1)  f[i][j]=min(f[i][j],f[i^(1<<j)][k]+w[k][j]);cout<<f[(1<<n)-1][n-1]<<endl;return 0;
}

二进制下不进位的应用

例题:起床困难综合症

给定n,mn,mn,m以及nnn个数和该数所对应的运算,其中运算有 与、或、异或 三种,问在所有不大于mmm的非负整数中,对给定的nnn个数都按该数所对应的运算运算一遍后,能得到得最大的值是多少。AND 表示按位与,OR 表示按位或,XOR 表示按位异或

输入样例:

3 10
AND 5
OR 6
XOR 7

输出样例:

1

注意到按位与、按位或、按位异或共有的一个性质:每次运算只有关该位上的数,不影响其它位上的数
所以我们可以从高位到低位来确定数的每一位。
如果该位可以填uuu,并且填uuu之后答案的该位是111,那么在该位填 uuu,否则填!u!u!u
那么如何判断该位能填几呢?如果该位填111后,所得到的数大于mmm,那么该位填!u!u!u
否则如果该位填111后,所得到的数对nnn个数都运算之后,结果小于等于该位填000后得到的结果,那么为了让剩下能填的数更大,该位填000。否则该位填111。

由于我们只需要得到填出来的数对所有数运算后的结果,而并不需要输出填出来的数,所以在写代码的时候并不需要真正的把数填出来,只需要确定是否能将答案的该位填成 111即可
一共要判断log⁡mlog⁡mlog⁡m次,每次判断是O(n)O(n)O(n)的,所以总的时间复杂度是 O(nlogm)O(nlogm)O(nlogm)

#include <bits/stdc++.h>
const int N=100005;
int n,m,ans,t[N],op[N];
char str[4];
bool calc(bool x,int j){    // calc用于计算 x 经过所有数的第 j 位操作后所得到的结果for(int i=0;i<n;i++)  if(op[i]==1) x=x|(t[i]>>j&1); else if(op[i]==2) x=x^(t[i]>>j&1); else x=x&(t[i]>>j&1);           return x;
}
int main(){scanf("%d%d",&n,&m);for (int i=0;i<n;i++){scanf("\n%s%d",str,t+i);if (*str=='O') op[i]=1;else if(*str=='X') op[i]=2;    else op[i]=3;}for (int i=29;~i;i--)                  // 因为本题中m最大是10^9,log2(10^9)=3log2(10^3)<3*10=30所以每次i从29往后枚举就可以了if(1<<i<=m){           // 如果填 1 后小于等于 m,要看填完后对答案的影响来填bool x=calc(0,i),y=calc(1,i); //先分别处理出该位填 0 的结果和该位填 1 的结果if (x>=y) ans=ans|(x<<i);//如果该位填1不比该位填0更优,为了让剩下能填的数更大,该位填0else ans=ans|(y<<i), m-=1<<i;//否则在该位填1填完后让 m 减去该位填 1 的结果}//这样在后面填数的时候只用考虑是否大于 m 就可以了else ans=ans | (calc(0,i)<<i);             // 否则该位只能填 0printf("%d\n", ans);return 0;
}

STL bitset运用

一、概念
bitsetbitsetbitset可以说是一个多位二进制数,每八位占用一个字节,因为支持基本的位运算,所以可用于状态压缩,nnn位bitsetbitsetbitset执行一次位运算的时间复杂度可视为n/32n/32n/32.

二、基本操作

bitset<n> s;
//表示一个n位的二进制数,<>中填写位数;
a.size()返回大小(位数)    a.count()返回1的个数
a.any()返回是否有1         a.none()返回是否没有1
a.set()全都变成1           a.set(p)将第p+1位变成1
a.set(p, x)将第p+1位变成x  a.reset() 全都变成0
a.reset(p)将第p+1位变成0   a.flip()全都取反
a.flip(p)将第p+1位取反     a.to_ulong() 返回它转换为ul的结果,如果超出范围则报错
a.to_ullong()返回它转换为ull的结果,如果超出范围则报错 a.to_string()返回它转换为string的结果
a.test() 获得指定位的值
s=101;// 存储为0001100101
cout<<s<<endl; //0001100101
cout<<s.to_ulong()<<endl;//101

例题:【bitset】无穷的序列

有一个无穷序列如下:110100100010000100000…请你找出这个无穷序列中指定位置上的数字。

第一行一个正整数N,表示询问次数;接下来的N行每行一个正整数Ai,Ai表示在序列中的位置。

#include<bits/stdc++.h> //板子题
using namespace std;
const int N=1e8;
bitset<N>s;
int idx=1,n;
int main(){for(int i=0;idx<=N;idx+=i++) s[idx]=1;scanf("%d",&n);while(n--){int x;scanf("%d",&x);printf("%d\n",s.test(x));}return 0;
}

用bitset做“起床困难综合症”

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0;
int main(){bitset<40> none,one ;none.reset(); // 一个初始化每一位都是0one.set();    // 一个初始化每一位都是1cin>>n>>m ;while(n--){string x;int y;cin>>x>>y;if(x=="AND") none&=y,one&=y;else if(x=="OR") none|=y,one|=y;else none^=y,one^=y;}for(int i=0;i<31;i++){if(none[i]==1) ans+=(1<<i);  //所以如果开始是0最后变成了1,(也就是伤害变多了)一定是需要选取的。else if(one[i]==1&&m>=(1<<i))  ans+=(1<<i); //如果开始是1最后变成了1,只要初始伤害值比1<<// (1在这一位的位数)大(也就是满足题目条件),显然也是要选取的。}cout<<ans<<endl;return 0;
}

【状压DP】易懂讲解状态压缩/状态压缩DP相关推荐

  1. bzoj 5248: [2018多省省队联测]一双木棋 博弈论+状压dp

    题意 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子当且仅当这个格子内没 ...

  2. POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)

    poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...

  3. [转]状态压缩dp(状压dp)

    状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴. 为了更好的理解状压dp,首先介绍位运算相关的 ...

  4. E. AC Challenge ACM-ICPC 2018 南京赛区网络预赛 状压dp + 枚举状态

    博客目录 原题 题目链接 Dlsj is competing in a contest with n (0 < n \le 20)n(0<n≤20) problems. And he kn ...

  5. 动态规划(DP),压缩状态,插入字符构成回文字符串

    题目链接:http://poj.org/problem?id=1159 解题报告: 1.LCS的状态转移方程为 if(str[i-1]==str[j-1])dp[i][j]=dp[i-1][j-1]+ ...

  6. ZOJ 3471 压缩状态DP

    这个问题要看状态怎么想,第一种直接的想法是1代表未合并,状态就从1111111 转移到 带有1个0,然后带有两个0, 但是这样子编程非常不直观.换一种思路,0代表未合并,但是我可以先合并前几个,就是说 ...

  7. 洛谷:P3092 [USACO13NOV]No Change G(状压+二分,独特的状态定义,不写会后悔一辈子的题)

    TP 一开始的思路是定义状态 dp[N][1<<16]dp[N][1<<16]dp[N][1<<16] ,维护的是能剩下最多的钱,但显然 n 那么大直接寄. 如何去 ...

  8. 【每日DP】day2、P1879 [USACO06NOV]Corn Fields G玉米地(状压DP模板题)难度⭐⭐⭐★

    昨天的每日DP我还在写01背包,今天就到状压DP了,真刺激. P1879 [USACO06NOV]Corn Fields G 题目链接 输入 2 3 1 1 1 0 1 0 输出 9 一道简单的状压D ...

  9. POJ 1038 Bugs Integrated Inc (复杂的状压DP)

    \(POJ~1038~~*Bugs~Integrated~Inc:\) (复杂的状压DP) \(solution:\) 很纠结的一道题目,写了大半天,就想练练手,结果这手生的.其实根据之前那道炮兵阵地 ...

  10. [BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)

    [BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关 ...

最新文章

  1. hdu 1272 小希的迷宫
  2. 每位开发人员都应铭记的10句编程谚语 (我超喜欢,转载了)
  3. 使用python函数计算3.5四舍五入的结果_python 数字的四舍五入-Go语言中文社区
  4. DL之DeconvNet:DeconvNet算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  5. Pyqt5 多标签_ESL电子货架标签方案
  6. 运动目标检测、阴影检测及目标跟踪中用得到的标准测试视频下载(大量IBM提供视频)...
  7. 【机器学习】坐标下降法(Coordinate descent)
  8. 远程删掉服务器的文件能找回吗,远程删除Windows服务器指定目录下N天前文件方法...
  9. 生物医学基础--讲不明白12导联算我输
  10. 记录一则数据库连接故障ORA-12560,ORA-12518
  11. # 2017-2018-1 20155336《信息安全技术》实验二——Windows口令破解
  12. OpenSSH概念和基本用法——SSH 客户端
  13. 201671030118 词频统计软件项目报告
  14. 软件能力成熟度模型(Capabilitymaturity model,CMM)
  15. C++在使用fgetc读取文件时出现方框乱码
  16. 射频百科:双工器是什么?双工器工作原理
  17. Windows网络活跃点决定使用的优先权
  18. 求几道C++题,不用排版,代码尽量简单,方便理解
  19. word文档加密、只读
  20. 使用jQuery,写一个简单的轮播图,实现切换功能!

热门文章

  1. 最速下降法 理论证明
  2. Clickhouse数据库web可视化工具Sqlpad
  3. 计算机考试多选试题及答案,计算机中级职称考试试题及答案 [2018职称计算机考试WPS_Office多选试题及答案]...
  4. SQL安装步骤及可能遇到的错误
  5. 安装EUCALYPTUS
  6. IIC协议超详细解释(适合小白入门)
  7. c2000 电阻采样_采样电阻规范阻值表
  8. 祝贺博主新浪微博个人认证与企业微博认证通过,欢迎关注!
  9. 用c语言编写一个打勾的图形,C语言图形编程.ppt
  10. 非主流文字转换_一篇搞定微信公众号文字排版(全干货,推荐收藏)