简介
状态压缩dp可以说是耳熟能详了吧,其实就是但我们类似于用搜索的思想找情况,之后对于每一个情况就可以当作一个状态去转移,在学习这个之前让我们先去学习一下位运算
位运算
“|” or 或运算 , 两个有一个是1就是1
“&” and 与运算 , 两个都是1才是1
“^” 异或运算 , 两个不同才是1
例题
P1896 [SCOI2005]互不侵犯
oi wiki上的例题,也是我第一个状压的题目
首先我们发现这个题目是一个类似于在棋盘上的计数,之后我们先不想状态压缩,直接dp的话如何dp[i][j][k]第i行j列放k个国王所有的方案数目,之后就会发现这个转移是不是要从上下左右四个方向进行,所以不太行。
之后就考虑列数不是很多,所以我们可以把每一行的所有合法情况都找出来,用二级制压位,之后就可以设计状态了dp[i][j][k]第i行用j这个方案一共放了k个国王的方案数,之后这个转移是不是就显而易见了,对于上一行进行每一个方案的枚举,之久就是转移,最后不要漏状态就行。
启示
其实这种设计状态的思路挺值得学习的,就是首先题目给了你的维度的状态都要想一想是不是可以假如设计(对于这题我认为状态里面最难想的就是k个国王吧)至于压缩状态其实就是发现正常的设计会有后效性,所以我们就状态压缩一下。
P2622 关灯问题II
是一个和一下的思路都不太一样的状态压缩,我们先去想一下这个是否可以把所有的可到达的状态都找出来,可以是可以但是不好找对吧,那我们可以看到这个所有的灯是很少的之后我们就考虑直接把所有的找出来,就是从((1 << n )- 1)开始枚举 从每一个状态开始看看他可到达的都有那些,之后取到达0的最小值,之后进行状态之间的转移
杂题
P3694 邦邦的大合唱站队
这是一个好题
首先70分的做法是把所有排列都找出来,之后用前缀和加速一下找不同,最后就好了,其实这个思路是很重要的。
代码放在这里
代码
之后就是正解
考虑一下上面的做法慢在哪里,首先就是找出所有的排列时间过于长,所以考虑如何优化,由于m的数量很少,就想一下状态压缩一下下
首先考虑dp[0101] (当然这里用十进制表示) 代表第1和第三排列好要的代价
之后就是转移假如说这一个乐队需要在这个状态里拍好那么就转移dp[i] = min(dp[i] , dp[i ^ (1 << (j-1))] + 要出队的数)。 这里我们只去枚举找最后一个被整理好的数的贡献,(这里其实就有一点之前找排列的意思了,只不过我们不关心顺序)
整理一下
首先我们找到把类似于10010这种状态排列好要的最小代价
之后就是转移,假如说这个是要被安排好的,我们就默认把他放在最后一个,之后其他的代价是不是就是之前已经找到了的,最后就只用前缀和加速并且取min就好了
这里我们相当于对于第一种解法不在意了各自的位置,可能就类似于dfs中神奇的枝剪
代码
P2704 [NOI2001] 炮兵阵地
其实是一个比较基础的题目,首先考虑一下把每一行的状态压缩(这个比较简答)
之后就是设计状态,考虑这个状态是要考虑往上两行,所以说我们直接设计dp[i][j][k]表示第i行上一行用j上上行用k的最大贡献,之后转移也是十分的简单,注意一下第一行和第二行要单独算就好了
至于山丘的处理其实就把每一行再加上一个二进制,山丘的位置为1,之后与一下就好了。
总而言之,就是一些小细节,其实也是比较简单的,还是上一个比较好玩