本篇随笔简单讲解一下信息学奥林匹克竞赛中的状态压缩动态规划相关知识点。在算法竞赛中,状压\(DP\)是很是常见的动规类型。不只如此,不只是状压\(DP\),状压仍是不少其余题目的处理技巧。因此掌握状压、掌握状压DP是十分重要的。html
注:虽然本身写的也是状压DP的讲解。但仍是凭良心推荐机房大佬@littleseven的状压博客,讲的真的是太详细了。强烈推荐!算法
连接Link:数组
浅谈状压DP函数
来看一个问题。优化
相信你们都作过动态规划的背包问题,那么咱们再来看一个跟背包很像的一个问题:spa
如今对于一个大小为\(V\)的背包,有\(n\)个物品,每一个物品有一个价值和一个体积。请问:每种放置方法能达到的价值。设计
最暴力的方法是\(n\)重循环,每重循环只有两次,一个是判断,一个是不判断,复杂度是\(O(2^n)\)的。htm
可是这种方法并非很是棒的,由于在编写程序的时候,循环的层数是肯定的。可是\(n\)是一个变量,若是非要按这个方法写的话,就会有一堆判断(分别判断\(n=1,n=2,n=3,...,n=maxn\))的状况,麻烦得要死。blog
那怎么办呢?get
咱们能够发现:对于每一种方法中的每种物品,都只有选或者不选两种选择。这就是一个决策过程,由于咱们要知道全部方式的价值,那咱们莫不如用一个二进制数表示这个状态“选仍是不选”,二进制数对应的位就表示这个物品选仍是没选,而后在二进制基础上进行计算价值。
因此枚举范围就是\(0-2^n\),从一个都不选到全都选。
那么咱们发现,本来须要开若干重循环和若干维数组的问题一会儿变成了用一个数组和一重循环就解决的问题了。
这个把状态压缩的过程就叫作状态压缩,在存储状态的基础上进行枚举转移的动态规划就叫作状压DP。
由于状压是在二进制的基础上实现的一门DP算法,因此它必定会和位运算相关。事实上,全部状压\(DP\)都须要选手具备高深的位运算技巧,熟练运用位运算取出或设置每个状态的判断条件和转移。
若是对位运算还不太懂的同窗,请参考这篇博客:
而且,咱们要对动态规划的过程有一个直观的认知。
有了这些知识,理解状压\(DP\)才会更加流畅、顺利。
经过刚才的例子(背包),咱们会发现,状压对算法的时空复杂度并无多少的优化,也就是说,它的本质实际上是暴力枚举。
由于是暴力枚举,因此枚举每一个状态的时间复杂度是\(O(2^n)\),这是一个指数级别的时间复杂度。高中数学必修一的函数部分告诉咱们,指数是爆炸式增加的,也就是说若是不加优化,时间立刻就会炸掉。因此基于这个性质,通常来说状态压缩的题数据范围不会超过\(20\),数量比较小。而基于这个性质,咱们设置状态的时候也就挑这个最小的数来设置。
先来看一道状压基础题(是洛谷蓝题,可是由于状压都比较难,因此不用发怵)
给定一个\(N*M\)的矩阵,要在这个矩阵中种草,规定入股矩阵的值俄日1就能够中,反之就不行。如今给出这个矩阵,请问它有多少种猪矩阵的方案。数据范围:(\(1\le N,M\le 12\))
看见没,数据范围很是小,这样的题往状压上想想。由于要统计方案数,因此咱们的状态就设置成:\(dp[i][j]\)表示枚举到第\(i\)行,第\(i\)行的状态为\(j\)时的方案数。那么答案就是第\(n\)行的全部状态方案数的和。
刚刚咱们已经说了,状压其实连优化枚举都算不上,它就是方便了咱们写枚举的循环代码,对复杂度并无多少贡献。基于这个性质,咱们直接开始从头枚举,依次递推便可。
但事实没这么简单,由于咱们还须要判断每一个状态是否合法:若是不合法,那这个状态就不能进行枚举。
因此咱们再想办法在转移的时候判断其到底合不合法便可。具体的实现请翻上面的题解。
状压\(DP\)算法的设计思路为:(敲黑板!!)
设计状态—>考虑转移的先决条件—>想办法维护这些条件—>进行转移—>统计答案。
以上为基础状压DP,如下为状压DP进阶