状态压缩动态规划中的状态与时间
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
状态压缩动态规划中的状态与时间
大丰市高级中学韩旭
目录:
【关键字】
【概述】
【正文】
1.基础知识
1.1 动态规划的概述 (1)
1.1.1 动态规划的概念 (1)
1.1.2 一些常见术语 (1)
1.1.3 和递推的区别 (1)
1.1.4 一些必要性质 (1)
1.2 状态压缩动态规划的使用动机及其特点
1.2.1 状态压缩动态规划的使用动机 (1)
1.2.2 状态压缩动态规划的特点 (2)
1.3 位运算的引入及其优化效果
1.3.1 位运算的引入 (2)
1.3.2 位运算的特殊用法及其优化效果 (2)
2.常见的状态压缩类型概述
2.1 常见集合型的状态压缩
2.1.1 含义 (2)
2.1.2 性能极其特点 (2)
2.1.3 例题 (2)
2.2 基于联通性的状态压缩
2.2.1 含义 (3)
2.2.2 性能极其特点 (3)
2.2.3 例题 (3)
2.3 小结
3.典型例题的优化及其效果
3.1 改变状态及其优化效果 (5)
3.1.1 含义 (5)
3.1.2 例题 (5)
3.2 去重消冗及其优化效果 (8)
3.2.1 含义 (8)
3.2.2 例题 (8)
3.3 小结
【参考文献】
【附件】
【关键字】
动态规划状态压缩去重消冗改变状态降低时耗
【概述】
随着社会的发展以及社会生产技术的变革,世界也在从工业化转向信息化。
与之而来的也就是信息学的快速发展,即其在生产生活中的大范围运用。
但是很多的实际问题到目前为止并没有太多十分有效的算法,也就是无法在多项式时间复杂度内得出结果,但却依然需要快速解决,状态压缩的概念也就被引入进来并用以解决特定的一定范围内的问题。
然而繁琐的状态压缩方法以及冗余的状态表述依然是制约效率的因素之一。
于是去重消冗与改变状态来提高时空效率也就是我们需要做的,通过下面题目的分析,希望能对大家起到抛砖引玉的作用。
(ps:基础知识可直接跳过,常见的状态压缩类型举例可以适当跳过)
【正文】
【第一部分】基础知识
1.1.1 动态规划的概念:
动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法。
动态规划是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。
其往往是针对一种最优化问题。
由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。
1.1.2 一些常见术语:
阶段,状态,决策
1.1.3 和递推的区别:
决策的存在
1.1.4 一些必要性质:
无后效性:对于状态,如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,所有各阶段都确定时,整个过程也就确定了。
最优子结构性质:要求问题的最优策略的子策略也是最优。
1.2.1 状态压缩动态规划的使用动机:
一般的状态描述不满足无后效性原则,或者保存的信息不足够进行决策。
将当前一部分局面信息压缩存储,结合常见的一些局面描述,使得构成的状态满足无后效性原则,从而可以实现动态规划来解决问题。
1.2.2 状态压缩动态规划的特点:
压缩后本身要满足动态规划的性质(最优性原理、无后效性)。
数据规模比较小,可以进行可行的压缩。
1.3.1 位运算的引入:
按位与运算 (and) 效果是逐位全一为一,反之为零。
按位或运算 (or) 效果是逐位有一为一,反之为零。
按位异或运算 (xor) 效果是逐位不同为一,反之为零。
1.3.2 位运算的特殊用法及其优化效果:
按位与运算(and)
可以取出二进制中的某些位,也可以取出二进制数的最后一位非零位。
按位与运算(or)
可以将二进制某些位上添上1。
按位异或运算 (xor)
可以将二进制某些位上的元素取反
【第二部分】常见的状态压缩类型及其压缩方式
2.1 常见集合型的状态压缩:
2.1.1 含义:
以一个集合内的元素信息作为状态,状态总数为指数级别的状态压缩动态规划方式。
(其通常带有较为明显的集合色彩,如在于不在,取或者不取,亦或是取的数量是多少多少这样的情况)。
2.1.2 性能极其特点:
这种方式往往是最简单最直接的状态压缩的方式,思考的复杂度不是很高,但在这类状态压缩中往往不同的压缩方式其效率差距很大,状态压缩方式的选择往往是影响这类方法效率的关键。
2.1.3 例题:炮兵阵地(NOI2001)
在N*M网格地图上部署炮兵部队。
每个炮兵可以
控制横纵2格范围。
任意一对炮兵互相不能处于控制
范围。
炮兵的火力覆盖范围为右图所示。
地图上有些点不能部署部队。
(N <= 100;M <= 10)
分析:
方案一:
首先这道题目中炮兵的射程只有横纵各两个格子而已,也就是说每个炮兵只会影响之后的两行。
此外对于任意一种炮兵安置方案中炮兵的安置顺序对全局不构成影响,因而我们可以逐行的放置炮兵,且只需要记录放置行的向上连续两行的状态。
于是我们可以设出这样的状态:
opt[i][j][k](j,k为二进制的表达形式)表示到第i行未放置之前第i-1行的状态为j以及第i-2行的状态为k的最大放置炮兵数目。
其状态的数量级为O(n*2^m*2^m)。
方案二:
如果我们不是从炮兵的存在与否来考虑状态的压缩,而是从每个炮兵放置后影响的范围进行考虑的话,我们可以设出这样的状态:
opt[i][j](k为三进制的表达形式)表示到第i行未放置之前,之前的每一列最下方的炮兵到当前行的距离集合为j(每个跑兵包括其本身格子在内最多向下延伸3行)的情况下的最大放置炮兵数目。
其状态的数量级为O(n*3^m)。
2.2 基于联通性的状态压缩:
2.2.1 含义:
除了以一个集合内的元素信息作为状态,在其状态中还需要记录若干个元素之间的连通情况,称为基于连通性状态压缩的动态规划问题。
(其通常与图形相结合,如线路、管道等等,通常是要求求得线路的数量或是最值)
2.2.2 性能极其特点:
这种方式往往是根据图形来进行一些状态压缩,往往就是用插头去进行拼接。
这类状态压缩的方式通常较为单一,状态压缩方式的决定对效率的影响不是很大,但消除重复运算和不必要运算往往对效率的提高有奇效。
2.2.3 例题:Formula 1 (Ural 1519)
给你一个m * n的棋盘,有的格子是障碍,问共有多少条回路使得经过每个非障碍格子恰好一次。
(m <= 12;n <= 12)
分析:
这是一道经典的插头DP,我们显然是逐行逐列的推导来划分状态。
对于每个格子里的线路会有以下的一些情况:
我们可以设出这样的状态f[i][j][k](k为一个三进制的表达形式,用以表示括号匹配来表示插头的联通状况)表示第i行第j列没有确定线路的状况下轮廓线上的插头情况为k时路线的方案数目。
每次我们可以把上述的6个方格中的某一个放入第i行第j列,使得线路得以延续推导出新的状态。
其状态的数量级为O(n*m*3^m)。
动态规划过程中转移时需要分类讨论插头方向。
当前格上方左方均有插头:只能将这两个连通块连接。
当前格只有上方有插头:将这个插头向下向右延伸。
当前格只有左方有插头:将这个插头向下向右延伸。
当前格周围无插头:若当前格为障碍物,则无插头,否则插入一个折线形插头。
对于第一种情况,需要合并连通块,若不加限制,则会计算出包含多条回路的情况。
(在我们和并连通块时,若两个插头属于同一个连通块,则当且仅当在最后一个有效格子中可以将这两个插头连接)
2.3 常见的状态压缩的小结:
以上就是两种常见的状态压缩类型及其压缩方式。
通常的压缩方式和它们是非常类似的,出现的题目类型也是两者中的某一种,虽然其中状态表述丰富繁杂,但万变不离其宗,其目的都是将复杂无序的状态压缩后用有序的数学结构进行表述以
便进行下一步的操作,实质都是一样的。
【第三部分】典型例题的优化及其效果
3.1 改变状态及其优化效果:
3.1.1 含义:
用状态压缩动态规划解决问题时,往往我们可以有很多种状态可以进行选择,不同的状态在状态的数量、转移的复杂度以及应用中的优缺点都是不同的。
于是往往有一类题目,我们最直观的想法及最直接的压缩方式,其空间复杂度和时间复杂度都很大,同时很难优化。
这时我们就要转化问题,转化到一些状态表述更为精炼、简便的问题,从而便于我们解题。
3.1.2
例题:局部极小值(cqoi2012)
【题意简述】
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。
如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
【分析】
这道题目具有很明显的状态压缩的味道,同时其数据规模非常的小,且具有常见集合型的状态压缩的特点,所以很容易联想到使用状态压缩来解决这个问题,但如何进行状态压缩呢?
首先我们需要考虑的是如何定义一个状态使得每次我们填入一个数之后做出
的结果没有后效性。
最直接最简单的想法就是和传统的状态压缩方式类似采用二进制来表示每个数的使用状况且逐行逐列的进行放置。
这样放置的好处显然就是每一步我们都可以知道哪些数已经用过了,哪些数没有用过可以放置在当前位置上。
但其不足也非常的明显甚至致命,就是没有办法表示其周边数的大小比较情况。
很显然一个格子和四周最多有四个格子相连,因为我们是逐行逐列进行放置,所以只需要关心每个数的上方和左方数的大小,对于左方的数我们可以进行记载,但上方的数我们就没有办法进行记录和压缩了。
这样的方法显然是解决不了问题的,于是我们必须寻找其它的办法。
方案一:
对于n*m的矩阵,其中有一维非常的小,至少不大于4,这是不是提醒我们可以把上一行的数记录下来呢?设想一下如果我们把上一行的数字记录下来的话,那么之后每填充一个数后必然就能知道其与之前的数的大小关系了。
但这样又带来了一个新的问题,就是我们没有办法知道当前填进格子里的数之前是否已经被使用过了,
这个问题是否可以解决呢?如果解决不了,必然这个状态也是无效的。
我们再进行思考不难发现,每个数在大小上只影响后面的一行,至于再后面的行里放的数就和当前的数没有关系了。
每当我们放完一行后,后面的可以放置的数必然都是不同的,因而也就具有的大小顺序。
于是我们考虑记录的就不是上一行放的是什么数了,而是上一行的数在后续数中排在第几位,考虑放置的也就不是具体的什么数了,而是放的数是后续数中排在第几位的数。
由于有一维只有4,算成轮廓线也只有5,所以
这样压缩成的状态再第一行有5
C种情况,第二行及其之后会更少。
于是我们初步
n
*m
拟定了这样的状态:opt[i][j][k][l](k为一个组合压成的状态,l为一个二进制表达形式)表示第i行第j列未放置数之前轮廓线上的若干个数在之后的数中的排名状态为k以及之前是否有数比轮廓线上数小的状态为l的情况下的方案个数。
如上图的状态就可以表示为opt[2][4][num1][num2](其中num2为10110的表示,num1为6、4、8、23、3组合在一起的压缩编号,10110含义就是6、9、25四周有比其小的值,6、4、8、23、3含义就是指6、4、9、25、3在红色轮廓线之后的数中的大小排名)。
这样的状态已经是一种满足我们需要的表述方式了,但细算下来,这样的状态因为需要记录之前是否有相邻的数比轮廓线上数小的情况,使我们的状态数目陡增不少。
如果没有"."必须为非最小值得限制,这样的压缩方法还是可行的,但对于这道题目,这样的压缩方式还是无法有效解决。
方案二:
换了个角度我们继续看问题。
对于一个合法的数填写方案,其中的数放置的顺序对其是没有影响的,于是我们可以从1开始填数,并且一个一个地填进格子。
如果采取这样的做法,那么所有的“X”必然要在其周边所有的格子填数之前就填好一个数,而"X"有多少呢?很显然最多只有8个而已。
这时我们就可以想到这样的一个状态压缩方式:opt[i][j](j是一个二进制表达)表示的是i及其以后的数还没有填进格子,被填写了数的“X”集合状态为j的情况下的方案数。
如上图4*7的矩阵中,红色的"X"表示已经填写数的"X",红色的格子表示已经填写数的非"X"格子,那么可以表述成这样的状态opt[8][num](8表示已经填写了7个数,下一个填写8,num是011010的表示,含义是第2、3、5个"X"已经填写了数了)如果我们转移的话就会有两种情况:
第一种情况就是把i填进一个"X"中,这个显然只要枚举一下放哪一个"X",然后把这个"X"加入j表示的集合里就可以了。
如上图,下一步我们填写"X"是可以随意的,因为只要存在解,任意的"X"都是互不影响的。
当前的状态为f[8][num1](num1为011010的表示),可以推导到
f[9][num2](num2为111010、011110、011011的表示)。
第二种情况就是把i填进一个非"X"中,这样的选择就有很多了。
对于全图我们一共有n*m个格子,若没有填进去数的"X"格子以及其周边的格子共有tot个,显然这tot个格子都是不能填i的(因为填进的是一个非"X",并且一个没有填进去数的"X"格子其周边因为都要比它小,所以这两者都不可以填i),又因为已经填写了1到i-1所有的数,所以剩下能填的选择数就是n*m-tot-(i-1)。
如上图,所有的蓝色区域都是无法填写i的,而下一步能填写的格子就只有白色的格子,即4*7-17-7=4个格子。
由于这样的处理方式,尤其是第二种转移可能会导致非"X"点变为最小值,所以还需要使用容斥原理来解决。
3.2 去重消冗及其优化效果
3.2.1 含义:
很多情况下我们的状态记录了一些不必要的状态,这些冗杂多余的状态不仅占用了不少空间,同时也消耗了很多时间。
去重消冗的方法就是从状态本身出发,在不对状态本身进行巨大变动的前提下尽可能去除重复的状态从而提高效率的方法。
由于状态压缩动态规划中状态的数量级别为指数级的,所以这里面的优化空间是很大的,因而这种不对状态伤筋动骨的方法往往是很有效的。
总之就是减少状态、简化转移以提高效率。
3.2.2
例题:迷宫改造
【题意简述】
给定一个n行m列的迷宫,相邻的两个单元之间存在一堵墙或者一扇门,墙是不可逾越的,而门是双向的且可以任意通过。
现已知不多于三对的起始点与终点,要求让尽量少的墙变为门后使得没对起始点与终点之间联通,且每对起点与终点之间的路径只能不断向右向下蔓延。
(3<=N,M<=20)
【分析】
对于这道题目的状态表述是非常容易想到的,就是opt[i][j][k][l][m]表示以i,j为转折点的轮廓线上,前后三个起始点分别联通到轮廓线上的第k、l、m个格子,并在这个情况下最少的墙变为门的数量。
这样的最大状态数为
20*20*21*21*21=3704400种。
如上图的就可以表述为opt[5][4][3][6][8](表示轮廓线上的转折点为第5行第4列的格子,第一个起点联通到轮廓线上第3个格子(红色),第二起点联通到轮廓线上第6个格子(蓝色),第三个起点联通到轮廓线上第8个格子(绿色))。
由于每次转折线左侧和上侧的点可以选择向下亦或是向右,转移的复杂度近似于O(1),所以最后的时间复杂度也为O(n*m*(m+1)^3)。
理论上来说这样的算法似乎已经可以满足我们的需要了,但实现这个算法的时候却不难发现,高维又数目众多的数组常数很大使得效率受到了影响,于是就要寻求优化来提高效率。
优化一:
其实这道题目启用类似传统连通性状态压缩动态规划的轮廓线是非常没有必
要的,因为联通的线段的数量很少。
虽然传统连通性状态压缩动态规划的轮廓线使得转移的复杂度达到最低,但同时也使得状态数量的增多。
我们不妨就原始一点,将轮廓线扳直,用opt[i][j][k][l]表述状态(三个起点分别联通到第i行的第j、k、l个格子的最优值)。
这样的状态虽然转移的复杂度略微提高,但状态数却少了一个数量级。
优化二:
如果我们采用4维的状态数,转移的话每个起始点连出的路径都需要向右或是向下。
但实际上是每一个状态我们只需要每次拓展一个路径使得一条路径只向右拓展,或者拓展所有的路径使得一行上所有的路径都向下拓展。
这样转移的正确性是非常显然的,而且可以使得转移得到化简和提速。
优化三:
去除一些没有必要转移的状态。
比如第一个起点从第5行开始,而前4行如果第一个起点存在路径的状态必然是非法,也是没有必要拓展的。
同样的如果第一个起点对应的终点在第5行,而第5行以后的状态中如果第一个起点还有路径延续的话必然也是非法的、没有必要拓展的。
这些优化不仅是在时间与空间上的,在转移上也进行了优化,从而使复杂度有所降低。
3.3 小结:
对于状态压缩动态规划的优化往往是根据题目来看,对于重复冗杂状态很多的就去消冗,对于转移较为复杂的就去尝试简化转移。
不同的状态其表述的复杂度以及转移的复杂度都是不同的,这两者常常需要综合的考虑。
复杂的状态表述也许它的转移会简单与快捷,而简单的状态表述,其转移也许就会复杂许多。
选择好的状态,采取有效的优化会使得最后的效率得到很大的提高。
这些东西并不是非常的深奥,关键就看个人的经验多少了。
【参考文献】
百度百科相关词条
《基于连通性状态压缩的动态规划问题》(陈丹琦)
《状态压缩动态规划浅谈》(来源网络)
【附件】
例题:局部极小值(cqoi2012)(数据与程序见附录)
效率对比:
数据 1 2 3 4 5 6 7 8 9 10
方案一<=
1s
<=
1s
<=
1s
<=
1s
<=
1s
无法
出解
无法
出解
无法
出解
无法
出解
无法
出解
方案二 <=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
例题:迷宫改造(数据与程序见附录)
效率对比:
数据 1 2 3 4 5 6 7 8 9 10
优化前 <=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
>
1s
<=
0.1s
>
1s
<=
0.1s
<=
0.1s
优化后 <=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
<=
0.1s
注:第6个点与第8个点答案长度较长。