本文来赏析hihoCoder上的一道题目“状态压缩”。我们知道,用动态规划解决问题的时候,状态的表示是很重要的一环。一般来讲,状态可以用一个数组来表示,但是对于有些问题,它们的状态中所包含的信息过多,如果要用数组表示的话,数组的维度会达到四维或以上,这时我们就需要使用状态压缩。状态压缩的核心思想是用二进制位来表示状态,用位运算来表示状态之间的转化。这样不仅减少了空间的消耗,而且提高了程序的效率。
一、题目
背景
小Hi和小Ho决定乘坐火车前往一座城市,但是不幸的是,他们并没有能够买到很好的火车票——他们只能够乘坐最为破旧的火车进行他们的旅程。
火车上的人非常多,以致于都没有足够的位置能让每一个人都有地方坐下来。小Hi和小Ho本着礼让他们的心情——当然还因为本来他们买的就是站票,老老实实的呆在两节车厢的结合处。他们本以为就能够这样安稳抵达目的地,但事与愿违,他们这节车厢的乘务员是一个强迫症,每隔一小会都要清扫一次卫生,而时值深夜,大家都早已入睡,这种行为总是会惊醒一些人。一旦相邻的一些乘客被惊醒了大多数的话,就会同乘务员吵起来,弄得大家都睡不好。
将这一切看在眼里的小Hi与小Ho决定利用他们的算法知识,来帮助这个有着强迫症的乘务员——在不与乘客吵起来的前提下尽可能多的清扫垃圾。
小Hi和小Ho所处的车厢可以被抽象成连成一列的N
个位置,按顺序分别编号为1..N
,每个位置上都有且仅有一名乘客在休息。同时每个位置上都有一些垃圾需要被清理,其中第i
个位置的垃圾数量为Wi
。乘务员可以选择其中一些位置进行清理,但是值得注意的是,一旦编号连续的M
个位置中有超过Q
个的位置都在这一次清理中被选中的话(即这M
个位置上的乘客有至少Q+1
个被惊醒了),就会发生令人不愉快的口角。而小Hi和小Ho的任务是,计算选择哪些位置进行清理,在不发生口角的情况下,清扫尽可能多的垃圾。
输入与输出
输入数据的第一行为三个正整数N
、M
和Q
,意义如前文所述。
输入数据的第二行为N
个整数,分别为W1
到WN
,代表每一个位置上的垃圾数目。
输出一个整数Ans
,表示在不发生口角的情况下,乘务员最多可以清扫的垃圾数目。
样例输入 | 样例输出 |
---|---|
5 2 1 | 201 |
二、列出状态转移方程
我们定义F(i,p1,p2,...,p(m-1))
表示当前已经完成了第1
个位置到第i
个位置的清理工作,并且p1
表示第i
个位置是否被清理(0
表示未清理,1
表示已清理)、p2
表示第i-1
个位置是否被清理……p(m-1)
表示第i-m+2
个位置是否被清理的情况下,最多可以清扫的垃圾数量。注意到,p1...p(m-1)
代表从第i
个位置往前的连续m-1
个位置。
假设我们已经求得了F(i,p1,p2,...,p(m-1))
的值,我们来讨论第i+1
个位置的清理情况:
如果不清理第i+1
个位置,那么我们在该位置的状态可以表示为F(i+1,0,p1,p2,...,p(m-2))
,并且由于我们没有在第i+1
个位置清理任何垃圾,所以我们清理的垃圾数量相比于第i
个位置是没有变化的;
如果要清理第i+1
个位置,我们首先要观察是否违反了题目的条件,即p1...p(m-1)
中1的数量是否小于Q
。如果不小于Q
,我们无法清理第i+1
个位置;如果小于Q
,我们可以清理,此时该位置的状态可以表示为:F(i+1,1,p1,p2,...,p(m-2))
,并且由于我们清理了第i+1
个位置的垃圾,所以我们清理的垃圾数量相比于第i
个位置增加了W(i+1)
。
综上所述,我们列出的状态转移方程如下:

因为每次状态转移都是从i
向i+1
进行的,所以我们只需要按照i
从小到大的顺序进行计算就可以了。最后,我们找到第N
个位置上对应的所有状态,这些状态中清理垃圾数量的最大值即为所求。
三、状态压缩
我们如果按照上面的方程求解,那么需要开辟一个维数为M
的数组,在M
达到两位数的时候显然是不太可能做到的。通过观察发现,对于p1...p(m-1)
中的每个p
,取值只有0或1两种可能,我们很自然地想到用长度为m-1
的二进制串来表示p1...p(m-1)
,其最高位代表p1
,最低位代表p(m-1)
。这样我们动态规划数组的维度可以被直接减少到二维。
此时,我们的方程可以直接写成F(i,j)
,其中i
的含义和之前相同,j
则是上述二进制串对应的十进制整数,其取值范围为0
到2^(m-1)-1
。
如果不清理第i+1
个位置,j
的变化相当于右移一位,第一位补0,即:

如果要清理第i+1
个位置,j
的变化相当于右移一位,第一位补1,即:

通过状态压缩,我们保存状态只需要开辟N*2^(m-1)
大小的空间。在遍历的时候,对于第i
行,讨论清理或不清理第i+1
个位置的情况,并且填充第i+1
行的相应位置。最后观察第N
行的所有位置中填充的数据,最大值即为所求。
四、代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|