Dynamic Programming

by prime21

NOIp篇

 既然都说了是NOIp篇,大概就是一些很简单的东西咯.

背包dp

  1. 背包dp可以说是常见的模型。比如:01背包,多重背包,完全背包。也许很多名词你不怎么熟悉。但是背包你一定是会的。
  2. noip2014就考了一道背包dp(愤怒的小鸟)。也考了其简单优化。

Warm Up

Tgop的背包

  众所周知, tgopknight是壕。他心血来潮组了一直足球队。

  现在他正在为队员们买背包而犯难。tgopknight有数不尽的钱,不过我们可以假设有M亿元。作为美学大师的tgop,定义了美观度。他的手下现在选了N个全黄金镶各种宝石的背包。第i个背包的价格是Mi亿元,美观度是Bi。当然tgop是一个十分任性的土豪,买第i个背包的时候,若手上的钱不足Ti亿元,她就不会买这个背包了。聪明无比的你,能否求出 tgop可以得到的最大美观度呢? 

solution

  如果作为一道考场题感觉并没有什么难度。只要考虑按几种方式排序然后看是否可以和暴力拍上就好了。然后发现最后的做法是:将T-M从小到大排序然后dp就可以了。

proof:

我们先来考虑一个问题,最少需要多少钱才能买下全部东西。

对于一种给定的排列,买第i个物品需要的最少货币数为Ai

 

A_i=max(M_i+A_{i+1},T_i)
Ai=max(Mi+Ai+1,Ti)A_i=max(M_i+A_{i+1},T_i)
A_i=max(A_{i+1},T_i-M_i)+M_i
Ai=max(Ai+1,TiMi)+MiA_i=max(A_{i+1},T_i-M_i)+M_i

至此很明显的能够看出,把T-M大的值放在前面选,得到的A1才会尽可能的小。想得到更严谨的证明可以考虑排序不等式。

树形dp

  1. 树形dp也是常见的dp类型
  2. noip2014,day1,鱼唇的我第二题竟然就是用的树形dp(好像干了什么不得了的事情就MLE了)
  3. 大体上来说树形dp一般是在dfs的时候dp的。然而也有一些大爷是用for循环虐树形dp的。[zzj大爷]

Warm Up

tgop的party

  众所周知, tgopknight是壕。他心血来潮组了一直足球队。

  在tgop为老板的这家足球公司里存在上下级关系,且保证每一个人只有一个上司,tgop先生是没有上司的。介于tgopknight的软妹子币多得花不完,她办了一场party,她认为在邀请名单中出现了一个人以及他的上司,这个party就是不愉快的。作为知心姐姐的tgop,给每一个人定义了一个欢乐值,第i个人个欢乐度就是Fi,她想知道,这场party的最大欢乐度是多少。

solution

  一道简单的树形dp,分类即可。没什么好说的。。

Medium

砍树

  “光头强又在砍树了!”在家里看《熊出没》的prime21的妹妹这样说。

  现在问题来了,给定一颗无根树T。我们定义如果一颗子树是好的,则有可以通过断开不超过k条边得到它。

  现在prime21想知道,对于给定的T和k,有多少棵树是好的。节点个数不超过100.

solution

  1. 论一个鱼唇的不能再离谱一点的想法:出这道题的时候本来是想当xdy说的A题的。结果发现并不能直接统计度数组合计数2333.
  2. 其实还是一道sb题。和常见的树形dp一样,我们令f[u][k]表示与根u相连的已经断开k条边的答案,乘法原理转移即可。
  3. 注意在最后统计答案的时候与根不相连的要多砍一条边

状压dp

  1. 状压dp的全称是状态压缩dp。事实上状压dp并非是一种特定的模型,而是由于状态过于复杂,我们需要通过特殊的方式进行表示,即状态压缩。
  2. 常见的状压方式有:二进制,康托展开,BFS

Warm Up

tgop的球队

 

  众所周知,tgopknight是壕。他心血来潮组了一直足球队。

  现在他的球队要参加宇宙杯。一共有2^N(N<=16)支球队参赛,由于参赛球队过多,宇宙杯只有淘汰赛。

  正所谓运筹帷幄,决胜千里,tgopknight大爷得到一张表,表的第i行第j列表示的是第i只球队与第j只球队打,第i只球队是否会赢(Y--表示赢,N--表示输)。

  淘汰赛可以用如下方式表示:

  

  可以发现,对于不同的起始排列,最后获胜的球队将会不同,tgop想知道对于所有球队而言,每只球队获胜对应的排列数是多少

solution

1.很简单的题直接状压dp,即可。如果超时?怪我咯?这样做的复杂度是:

O(n^2 \ast 2^{2n})
O(n222n)O(n^2 \ast 2^{2n})

2.还真的可能超时。最后一次转移强行暴力即可。一看就是你没有经历一些刻骨铭心的东西啊。复杂度

O(n^2 \ast 2^n)
O(n22n)O(n^2 \ast 2^n)

Easy

手办

   prime21的手办并不是很多,今天他买了一个新手办。

  由于prime21喜欢的角色并不多,所以他可能买了同一个角色的几个手办,只是高度有所不同。

  现在prime21把所有手办排成一排,这个排列有点长。排列的长度为N(N<=1000),prime21一共有k种手办[每一个人物算一种](k<=7),给你排在第i的手办的高度Hi,和类型KINDi,现在他想知道完美手办子序列有多少个?一个手办子序列是完美的,当且仅当所有种类都在子序列中出现过一次,且这个子序列中每一个手办的高度都是递增的。

solution

  1. 同样是没什么难度的状压dp,大家理性愉悦就好。如果是最长的,大约可以变出个n log n算法..
  2. 其实考虑计数问题依然可以搞个 n log n 记录一个 dp关于h的树状数组即可。
  3. 然而对于k种物品必须出现,你可以尝试容斥/状压。复杂度相同,容斥空间消耗更小,速度更快。

Advance

  果然noip,难度还是太水了。

  再讲一点稍微有难度的东西?

论如何把dp水题变得稍微难一点

  1. 强行加一点维护的东西,比如开始的有限制的01背包,以及有限制的 IS问题。
  2. 加一些算法:二分转用dp判定,cdq+dp,dp+dfs转移,特殊的状压技巧
  3. 强套数据结构:维护凸壳,线段树,后缀数据结构,平衡树。
  4. 发现方程的一些特性,四边形不等式,斜率优化,单调性dp,SPFA优化dp。

玄幻?一个迷惑你眼睛的转移方程

  不管有没有迷惑你,反正我开始时被迷惑了。已知如下转移方程,对于给定n,m,求dp[n][m]。其中有dp[0][0]=0。

dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1
dp[i][j]=max(dp[i1][j],dp[i][j1])+1dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1

直接模拟就是N^2暴力,如何优化?

优化个鬼啊?你打表看看。

dp[i][j]=i+j
dp[i][j]=i+jdp[i][j]=i+j

小剧场结束,马上转入正题。^_^

还是背包dp!

  1. 是的。你没有看错,这里我们还是讲背包dp。背包dp任然有存在优化之处。
  2. 背包dp通常也可以套在一些奇怪的模型里面出现

背包优化

  1. 01背包可以空间优化
  2. 如果单单按照01背包的套路暴力解决多重背包和完全背包问题时间复杂度较高,往往不能取得满分。
  3. 下面稍微讲一下优化。

完全背包

1.我们考虑完全背包的dp方程:

f[i][j]=max(f[i-1][j-k*v]+k*w,f[i-1][j])
f[i][j]=max(f[i1][jkv]+kw,f[i1][j])f[i][j]=max(f[i-1][j-k*v]+k*w,f[i-1][j])

2.其实我们可以改写此方程:

f[i][j]=max(f[i-1][j],f[i][j-w]+v)
f[i][j]=max(f[i1][j],f[i][jw]+v)f[i][j]=max(f[i-1][j],f[i][j-w]+v)

空间优化:对于01背包,我们每次倒序枚举剩余空间,不需要额外的一维来存储上一次的答案。

实际上就是在递推的时候顺序进行,即可以利用之前已经选了该物品的背包继续放该物品

多重背包

  相比之下多重背包的限定了放置个数,优化难度更大,但是我们要相信,算法是可以优化的!

level 1 二进制优化

  我们原有的思想是把一个可以使用k次的物品,拆分成可以k个只能使用一次的物品。

  然而我们可以进一步思考?对于1~k中的所有可能情况,是否用更好的表示方法表示所有的信息呢?

  我们考虑用形如                   的数的0,1组合恰好可以表示1~k之间的所有整数。所以我们考虑可以拆分成               个物品,每个物品的权值是原有基础上的     ,最后一个是           。其中m是能满足               的最大整数。

1,2,4, \cdots ,2^i
1,2,4,,2i1,2,4, \cdots ,2^i
O(\log K)
O(logK)O(\log K)
2^i
2i2^i
k-2^m
k2mk-2^m
k>=2^m
k>=2mk>=2^m

level 2单调队列优化

  通过二进制优化之后的多重背包的时间复杂度是                         ,但我们期待所有的背包问题,都可以在               解决。

O(N*M* \log K)
O(NMlogK)O(N*M* \log K)
O(N*M)
O(NM)O(N*M)

单调队列优化

f[j]=f[j-k*weight]+k*val (0 \leq k \leq count)
f[j]=f[jkweight]+kval(0kcount)f[j]=f[j-k*weight]+k*val (0 \leq k \leq count)
f[j*weight+mo]=f[k*weight+mo]+(j-k)*val
f[jweight+mo]=f[kweight+mo]+(jk)valf[j*weight+mo]=f[k*weight+mo]+(j-k)*val
f[j*weight+mo]=f[k*weight+mo]-k*val+j*val
f[jweight+mo]=f[kweight+mo]kval+jvalf[j*weight+mo]=f[k*weight+mo]-k*val+j*val

Dynamic Programming

By prime21

Dynamic Programming

Dynamic Programing

  • 2,352