帮助——状压

帮助


这道题好题,接下来咱们从头至尾详细地分析一遍这道题的作法以及给咱们之后作题目的启发。数组


考虑到\(25\)\(32\)这一值域很小,而且能够总体平移到\(0\)\(7\),这是原始想法。优化


接下来积累一个比较好的想法:将一个数抽出来再放回去至关于仅仅把抽出来的数一股脑取出来,最后再进行插入操做spa

该转化有何好处呢?get

  1. 能够将复杂的问题分步解决;2. 转化为较为通用的问题:也即,咱们将原问题转化为了从一个序列删一些数,再加一些数求最值。

该转化能够被普遍地应用。class

回到本道题,咱们能够将问题视做:先从这一堆数中取走若干个数,再将这若干个数插回去,在该过程当中,若是取出的一些在数值上相等,按照贪心来说,插入回去的时候一定在一块儿,由于这样最优。统计


有了这样的想法,咱们不妨定义状态:总结

\[dp(i,j,0/1) \]

分别表明前\(i\)个数,还剩\(j\)次操做能使用,是否被取走的最小混乱度。数据

接着咱们转移,发现:集合

  1. 诶?若是咱们选择取走,那么直接转移,可是最小混乱度还须要加上咱们取走的数从新加入原序列中形成的混乱度;
  2. 那若咱们不取走,就会发现若上一个数选择取走,咱们整个状态转移没办法进行。

所以,咱们不难想到能够更加细致化状态。di


再考虑数据范围,诶?\(1\)~\(8\)?这么小。

咱们能够给原来的状态加一个维度.

\[dp(i,j,S) \]

其中\(S\)表明包括\(i\)在内前面被取走数的集合。

那么咱们有:

\[dp(i+1,j+1,S\cup\{a_{i+1}\})=min(dp(i,j,S))\\dp(i+1,j,S)=min(dp(i,j,S))+w \]

第一个方程很容易理解,而第二个方程中的\(w\)是啥?

咱们考虑,当\(a_{i+1}\)\(a_i\)数值相同时,\(w\)就等于\(0\),若是不相同,\(w\)就等于\(1\)

且慢

若是\(a_i\)这个数被取走了呢???咱们便不是这么好办了。

于是,咱们须要再加一维进行描述。

因此,咱们能这样吗:

\[dp(i,j,S,0/1) \]

集百家之所短

它的问题出在了咱们没办法直接肯定上一个保留的数是否与之相等。

那么,咱们这样呗:

\[dp(i,j,S,pre) \]

\(pre\)表明上一个被保留的数的位置。

已经能够搞了。


\[dp(i+1,j+1,S\cup\{a_{i+1}\},pre)=min(dp(i,j,S,pre))\\dp(i+1,j,S,i+1)=min(dp(i,j,S,pre))+(if\ (a_{pre}\not=a_{i+1})1;\ else\ 0) \]

那么,这个式子就能够经过了吗?别忘了滚动数组啦。

那最终答案怎么表示呢?

好问题,不太好解决——保留的数的混乱度加上取走的数从新排队的混乱度,咱们记录的集合\(S\)好像不能记录保留的数中有与之相等的数。由于若是原序列中有相等的数,那么取出来的数最优的状况是和该数接在一块儿生活。

第二个思想:补集转化。

既然咱们知道取出来的数集合做为状态的一维不利于统计答案,那么咱们可不可使保留的数做为状态的一维?

这是能够实现的。而且答案就是全部数值的集合减去未被取出来的数值集合剩下的数各成一峰。

总的时间复杂度为:\(O(n^2*k*2^7)\),卡一卡常就过了。


其实进一步优化:

考虑到咱们最后那一维其根本咱们是来解决上一个未取出的数的数值是否与当前不取出的数相等。

由于数的值域那么小,咱们仅记录上一个数的数值是多少便可。\(O(n*k*2^{11})\)


咱们总结一下:

  1. 咱们考虑到原问题的值域较小,所以咱们平移该值域;
  2. 将取出一个数再放回去的动做转化;
  3. 将状态精细化,这也是DP常见的作法;
  4. 补集转化思想,将取出来转化为不取出来;
  5. 状态精简。

这个问题就这样迎刃而解了。

相关文章
相关标签/搜索