转自 http://blog.csdn.net/helihui123/archive/2009/11/16/4814530.aspx
动态规划总结
写在前面的话:
1 、本总结的题目均出自vijos 动态规划分类和其他著名题库。每道例题都添加了对应的vijos 题 目的地址链接,可以通过ctrl+ 单击进入题目。读者在阅读本文以后可以将这些练习题温习以充实提 高。
2 、本文以例题为主,一 些具体定义可能会有所偏差,但并不影响算法的正确性。文章最后给出了一些完整定义,如果有兴趣可以阅读。
3 、如果本文有所疏漏, 请与作者联系(QQ:397740194) 。本人将不胜感激。
动态规划小引 :
算法 : 动态规划是记忆化搜索的循环结构形式(枚举子结 构)。动态规划的优势是思路清晰,调试便捷并且效率比一般搜索要高(加强剪枝除外)。
特征 : 动态规划的思路是由初始状态通过状态转移达到目标 状态。这便意味着可以运用动态规划解决的问题必须具备以下特征:
(1) 后面的状态不会对前面的状态产生影响,即无后效性。
(2) 局部最优会通过转移使全局达到最优,即最优子结构。
区别于贪心法 : 运用动态规划解决的问题由于具有最优子结构,通常 会误用贪心法解决。实际上,贪心法和动态规划的主要区别就是状态转移。贪心法是不需要状态转移的,因为它的每一步的最优结果都必然导致最终结果的最优。然 而动态规划的精髓就是状态转移方程。有了方程,实现程序便是小菜一碟了。
第一节 线 性动态规划
一、极值问题
1 、背包问题:
想必大家最一开始接触动态规划都是从01 背包开始的。
例题1 :01 背包问题 :
给出N 个 物品的体积和价值,用有限包的容量装最大价值的物品。
对于当前状态,可以用两个变量表示:[I,num](i 代表第i 个物品,num 代表使用的容量) 。状态转移就通过每个背包来进 行。因为每个背包只有两种情况:使用或不使用。因此我们得出dp[I,num] 的转移来源:dp[i-1,num] 和dp[i-1,num-v[i]](v[i] 代 表第i 个物品的体积) 。从而得出转移方程:
max( dp[i-1,j] , dp[I,j-v[i]] ) j>=v[i]
Dp[I,j]:=
Dp[i-1,j] j< v[i]
Dp[0,0]:=0;
可以看出,01 背包是通过一个约束条件得出最大值。而广义的背包问题的约束条件可以不止1 个约束条件。
例题2 : NASA 的食物计划
这道题就是通过对体积和质量这两个条件的约束, 要求出最大值。方程和01 背包问题很类似:
Dp[I,j,k]:=max(dp[i-1,j,k],dp[i-1,j-v[i],k-m[i]]+calori[i]); j 是体积 ,k 是质量
边界条件和01 背 包问题基本一致。
* 通过对以上两题的总结,我们得出了一类背包问题的 通解 :
Dp[i,j1,j2...jm]=
Max |
dp[i-1,j1-v1[i],j2-v2[i]...jm-vm[i]]+num[i]
在某些情况下 , 题目要求输出最小值 , 那么将 max 改为 min 即可。
2 、常 见简单一维动态规划问题:
例题4 : 导弹拦截 ( 第一问)
这道题第一问就是经典的 “最长不升子序列”问题。我们考虑,对于到当前一位的最长的不 升子序列,只能通过之前一个数值大于等于他的那一位的最大结果值加一得到。于是我们得出方程:
Dp[i]:=max(dp[j]+1)(1<=j=num[i])
* 此类问题均是一维线性dp, 十 分简单,方程可以通过简单的分析或者找规律求得。
3 、旅 行问题:
这类问题通常需要求从一个点走到另一个点的最 大、小值。通常情况下,这类问题是有分层结构的,因此这种情况下运用动态规划解决要比求最短路思路更为清晰。
例题5 : 小胖办证
这道题是求用最少耗费在矩阵中从一点到另一点。 对于每个点,来源至多有3 种:左、右、上。因此状态转移有三种。对于当前状态:第i 行,第j 列,有(i-1,j),(I,j-1),(I,j+1) 三 种前驱。因此写出方程
Dp[I,j]:=min(dp[i-1,j],dp[I,j-1],dp[I,j+1])+num[I,j]
需要指出的是,对于同层转移,由于方向不同,导 致具有后效性,但是单独一个方向仍然满足动态规划的特征,因此我们需要进行两次方向不同的dp ,并 取最小值即可。
例6 : 旅行 商简化版
由于这道题的旅行路线具有方向性,因此我们就利 用x 坐标的单调性来求解。我们用dp[I,j] 存 储第一个人走到I, 第二个人走到j 的最短路 程和。显然要i>j 。当然我们要预处理dist[I,j] 为i 到j 的坐标距离,即sqrt(sqr(xi-xj)+sqr(yi-yj)) 。
易见dp[I,1]=dp[i-1,1]+dist[i-1,j] 。 可以进一步求得当j
当i=j+1 的 时候,我们可以想象成将第一个人固定在i-1 处,第二个人从k 走 到i ,然后两人互换位置。于是:
Dp[I,j-1]:=max(dp[i-1,k]+dist[k,i]) 。(1<=k
最后的答案就是dp[n,n-1]+dist[n-1.n] 。
例题7 :Canada Tour(USACO Chapter5)
关于这道题的解答,在网上遍地都是,我就不再卖 弄Rp 了。^.^
例题8 : 天堂的馈赠
这道题的题意不好理解。在彻底理解题意的前提 下,我们仍然不容易想到将它转化为取数类Dp 。不过,我们可以换一种思路去考虑。我们可以做出一个 大表,横轴是天堂的宽度,纵轴是时间。在时间T 的格子存放的是T 时刻掉到底部的物品的价值。于是我们从第一行的第p 格 出发取数,直到走到第T 行为止。求第T 行的 最大值即可。
4 、最 大子图案类问题。
这种问题多是求在一个图形里面的满足条件的子图 形,有时候要求最大面积,有时候仅仅是判断是否存在。这类问题都可以用动态规划轻易地解决。
例题9 : 盖房 子
这道题很简单(可与一维背包相比)。对于第i 行,第j 列的点,存储dp[I,j] 为以该点向左上方所做的最大的正方形的边长。
Dp[i-1,j]
Dp[I,j-1]
容易看出 ,
dp[I,j]=min(dp[i-1,j],dp[I,j-1])+1([i,j] 满足要求)
dp[i,j]=1 ([i,j] 不满足要求)
最后输出 dp[I,j] 中的最大值即可。
例题10 : 迎春 舞会之集体舞
这道题和盖房子很类似,我们可以利用三角形的特 点来解决。由于三角形图案的两种形式具有对称性,我就先对向下的三角形进行处理,向上的三角形的情况读者可以同理解决。
我们考虑,对于每一个向下的三角形点[i,j] ,存储以它为顶点构成的最大的向下三角形dp[i,j] 。
可以发现,当[i,j] 不 为 ’ - ’ ;或者[i-1,j] 不为 ’ - ’ 时,最大的边长为1 。
否则dp[i,j]=min(dp[i-1,j-1],dp[i-1,j+1])+1 。
二、可行性问题
此类问题多用bollean 存储结果,因而空间复杂度较高,然而此类问题若不用动态规划只能用搜索做,不易剪枝,因此时间复 杂度相对要小很多。
此类问题主要包括积木城堡、新年趣事之打牌、猫 狗大战、邮票面值设计、
这类问题相当简单,我就不浪费字节了。嘿嘿 *.^
第二节 树 状动态规划
一、树状dp 的 动机:
当数据结构不再是理想中的线性的,而是树状的时 候,我们不愿去进行低效的搜索,希望用记忆化来帮助优化时间效率。于是----- 树状DP 诞生了……
二、树状dp 的 运算方式:
树状dp 采 用dfs 模式,从root 出发来进行运算。
三、树状dp 的 程序模式:
Procedure dfs(v:integer);
Var
I:integer;
Begin
For i:=1 to n do if father[i]=v then
Begin
Dfs(i);
Dp[v] ç Func(dp[i]);
End;
End;
四、树状dp 例 题讲解
例题11 : 小胖 守皇宫
考虑这道题对于一个节点I 的最小值dp[i], 必然由i 的子节点进行控制。而每个点向下只有三种情况:这个点设置守卫、这个点不设置守卫但下面有一个点控制这个点、 这个点不设置守卫且下面没有点控制这个点。对于这三种状态,我们分别存为1,2,3 。
对于dp[I,1] , 我们只要找子节点的最小值的和即可。
对于dp[I,3], 我 们只要求子节点中状态1 的和即可。
重要的是dp[I,2] , 因为这个点如果不设置守卫,只需要在子节点中设置一个守卫就足够了。于是我们先求子节点中状态2 、3 最小值的和,然后再找子节点中状态2 到状态1 的最小增量(delta) 即可。
给出程序的核心部分:
procedure dfs(v:longint);
var
i,s1,s2,s3,minn:longint;
leaf:boolean;
begin
s1:=0;s2:=0;s3:=0;leaf:=true;minn:=maxint;
for i:=1 to n do
if fa[i]=v then
begin
leaf:=false;
dfs(i);
s1:=s1+dp[i,2];
s2:=s2+min(dp[i,3],dp[i,2]);
s3:=s3+min(min(dp[i,1],dp[i,2]),dp[i,3]);
minn:=min(minn,dp[i,3]-min(dp[i,2],dp[i,3]));
end;
if leaf then
begin
if fa[v]<>0 then dp[v,1]:=0
else dp[v,1]:=maxint;
dp[v,2]:=maxint;
dp[v,3]:=num[v];
end
else begin
dp[v,1]:=s1;
dp[v,2]:=s2+minn;
dp[v,3]:=s3+num[v];
end;
end;
第三节 动态规划解题及应用技巧
一、 多进程Dp
这类题目的典型例子就是三取方格数。由于我们发 现,在以原点为一侧的各组斜线上的每个点代表着第x 次取到的所有点的集合。因此我们的状态转移就转 化为各组斜线之前的关系。对于每组斜线的状态有2^k 种,通过枚举当前状态以及寻找前驱状态来求 解。
二、 滚动数组优化内存
这种优化方法多用于状态i 只与状态i-1 有关的一类问题。比如装箱问题、LCS 等等。使用方法十分简单:
For i:=1 to n do
Dp[odd(i),f(x)]=dp[odd(i-1),g(x)]+h(i);
定义数组的时候可以
Var
Numset:array[boolean,1..maxn] of integer;
如此定义。因为boolean 同样是一个子界,因此是正确的。
三、 DFS+DP
这类问题比较少见,主要是先dfs 枚举可行方案或者可行解,再通过dp 来求解可行 性。可以通过做Vijos 的“邮票问题”来加深印象。
附录
动态规划的术语
1 .阶段
把所给求解问题的过程恰当地分成若干个相互联系 的阶段,以便于求解,过程不同,阶段数就可能不同.描述阶段的变量称为阶段变量。在多数情况下,阶段变量是离散的,用k 表示。此外,也有阶段变量是连续的情形。如果过程可以在任何时刻作出决策,且在任意两个不同的时刻之间允许有 无穷多个决策时,阶段变量就是连续的。
在前面的例子中,第一个阶段就是点A ,而第二个阶段就是点A 到点B ,第三个阶段是点B 到点C ,而第四个阶段是点C 到点D 。
2 .状态
状态表示每个阶段开始面临的自然状况或客观条 件,它不以人们的主观意志为转移,也称为不可控因素。在上面的例子中状态就是某阶段的出发位置,它既是该阶段某路的起点,同时又是前一阶段某支路的终点。
在前面的例子中,第一个阶段有一个状态即A ,而第二个阶段有两个状态B1 和B2 ,第三个阶段是三个状态C1 ,C2 和C3 ,而第四个阶段又是一个状态D 。
过程的状态通常可以用一个或一组数来描述,称为 状态变量。一般,状态是离散的,但有时为了方便也将状态取成连续的。当然,在现实生活中,由于变量形式的限制,所有的状态都是离散的,但从分析的观点,有 时将状态作为连续的处理将会有很大的好处。此外,状态可以有多个分量( 多维情形) ,因而用向量来代表;而且在每个阶段的状态维数可以不同。
当过程按所有可能不同的方式发展时,过程各段的 状态变量将在某一确定的范围内取值。状态变量取值的集合称为状态集合。
3 .无后效性
我们要求状态具有下面的性质:如果给定某一阶段 的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,所有各阶段都确定时,整个过程也就确定了。换句话说,过程的每一次实现可以用一个状态 序列表示,在前面的例子中每阶段的状态是该线路的始点,确定了这些点的序列,整个线路也就完全确定。从某一阶段以后的线路开始,当这段的始点给定时,不受 以前线路(所通过的点)的影响。状态的这个性质意味着过程的历史只能通过当前的状态去影响它的未来的发展,这个性质称为无后效性。
4 .决策
一个阶段的状态给定以后,从该状态演变到下一阶 段某个状态的一种选择(行动)称为决策。在最优控制中,也称为控制。在许多问题中,决策可以自然而然地表示为一个数或一组数。不同的决策对应着不同的数 值。描述决策的变量称决策变量,因状态满足无后效性,故在每个阶段选择决策时只需考虑当前的状态而无须考虑过程的历史。
决策变量的范围称为允许决策集合。
5 .策略
由每个阶段的决策组成的序列称为策略。对于每一 个实际的多阶段决策过程,可供选取的策略有一定的范围限制,这个范围称为允许策略集合。允许策略集合中达到最优效果的策略称为最优策略。
给定k 阶 段状态变量x(k) 的值后,如果这一阶段的决策变量一经确定,第k+1 阶段的状态变量x(k+1) 也就完全确定,即x(k+1) 的值随x(k) 和第k 阶段的决策u(k) 的值变化而变化,那么可以把这一关 系看成(x(k) ,u(k)) 与x(k+1) 确定的对应关系,用x(k+1)=Tk(x(k),u(k)) 表 示。这是从k 阶段到k+1 阶段的状态转移规 律,称为状态转移方程。
6 .最优性原理
作为整个过程的最优策略,它满足:相对前面决策 所形成的状态而言,余下的子策略必然构成“最优子策略”。
最优性原理实际上是要求问题的最优策略的子策略 也是最优。让我们通过对前面的例子再分析来具体说明这一点:从A 到D ,我们知道,最短路径是A à B1 à C2 à D ,这些点的选择构成了这个例子的最优策略,根据最优性原理,这个策略的每个子策略应是最优:A à B1 à C2 是A 到C2 的 最短路径,B1 à C2 à D 也是B1 到D 的最短路径……事实正是如此,因此我们认为这个例子满足最优性原理的要求。
转自 http://hi.baidu.com/xwf_like/blog/item/6b10f11a485c44d3ad6e75d9.html
在信息学竞赛短暂的历史中,伴随着计算机科学领域的突飞猛进,算法和数据结构的难度和广度都不断地发展,从网络流、平衡树,新的算法和数据结构不断地被纳入大纲。动态规划无疑是其中一颗璀璨的明星,在信息学竞赛的舞台上绽放出夺目的光彩。IOI94的《数字三角形》,第一次将动态规划这个名词刻进了信息学竞赛的历史。而NOI历史上的第一道动态规划题目出自NOI95的《石子归并》。自此之后,动态规划就一发而不可收拾,成为了近年来NOIP、NOI以及IOI必需掌握的内容。然而,学生在学习动态规划时总是不得要领,没能发挥出动态规划对于解题产生的巨大魅力。下面,本人根据多年的教学经验,提几点有助于大家学好动态规划的意见,不当之处,希望大家批评指正。
一、明确什么是动态规划
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
以 上的解释或许过于专业,在高中的信息学竞赛中,我们可以把动态规划理解为将一个问题的求解过程分为几个部分,并逐一求得最优值,从而得到全局最优值的方 法。举个简单的例子来说明:假设我们要以最高的利润卖出某样物品,我们就可以把整个过程分为两个部分——进货和销售。如果能以最低的价格进货,以最高的价 格出手。毫无疑问,我们便可以得到最高的利润。
二、明确动态规划的条件
任何一个算法都有其局限性,同样,不是每一个问题都可以用动态规划解决的。关于动态规划的条件我们参考以下方奇在2000年国家集训队中的描述:
1 最优化原理
最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。
最优化原理是动态规划的基础,任何问题,如果失去了最优化原理的支持,就不可能用动态规划方法求解。
2 无后效性
“过去的步骤只能通过当前状态影响未来的发展,当前的状态是历史的总结”。这条特征说明动态规划只适用于解决当前决策与过去状态无关的问题。状态,出现在策略任何一个位置,它的地位相同,都可实施同样策略,这就是无后效性的内涵。
由上可知,最优化原理,无后效性,是动态规划必须符合的两个条件。
三、理解一个概念
在分析出问题能用动态规划求解之后,实现动态规划的程序往往非常简洁,尤其是在写出状态和转移方程之后,实现起来非常容易。现在问题的关键就是找出问题的状态和转移方程。要找出状态和转移方程,必须先理解一个概念:
多阶段决策问题
如果一类活动过程可以分为若干个互相联系的阶段,在每一个阶段都需作出决策(采取措施),一个阶段的决策确定以后,常常影响到下一个阶段的决策,从而就完全确定了一个过程的活动路线,则称它为多阶段决策问题。
各 个阶段的决策构成一个决策序列,称为一个策略。每一个阶段都有若干个决策可供选择,因而就有许多策略供我们选取,对应于一个策略可以确定活动的效果,这个 效果可以用数量来确定。策略不同,效果也不同,多阶段决策问题,就是要在可以选择的那些策略中间,选取一个最优策略,使在预定的标准下达到最好的效果。
前文已经说过,动态规划相当于把一个问题划分成若干个部分,对于每个部分分别进行计算,然后得出全局最优情况。所以阶段就是我们划分成的部分,而状态便是我们所进行到的部分,从一个状态经过一个阶段达到另一个阶段,我们就称作状态转移。而在状态转移的过程中,我们常常有很多途径可以选择,这一个选择的过程就叫做决策。
以最长上升子序列为例。我们以已考虑的数字数来划分阶段,以F[i]表示以第i个数结尾的最长上升子序列长度来表示状态,由此可知,F[i]必定是由F[j](i-1=>j>=1)推出来的,若以num[i]表示第i个数的数值,则状态转移的条件便是num[i]>num[j],否则不满足上升序列的条件。于是我们得出了状态转移的方程:
其中F[1]=1。
四、重点研究几类经典问题
在理解了动态规划的一些概念之后,要能做到用动态规划解题,其中重点研究几类经典问题是很有必要的。就近几年的比赛题目而言,往往都可以转化为我们已知的经典模型。下面介绍几个经典模型:
1、按状态类型分
编号(长度)动态规划类,题库:最长不下降子序列;拦截导弹(noip99);花店橱窗布置(ioi99)。
区间动态规划类,题库:石子合并;邮局(ioi2000)。
坐标动态规划类,题库:棋盘分割(noi99)。
数轴动态规划类,题库:01背包;装箱问题(noip01)。
树型动态规划类,题库:选课(ctsc97),贪食的九头龙(noi02)。
集合动态规划(状态压缩)类,题库:购物(ioi95)。
2、按转移方式分
存在性:01统计(ctsc99);Catalan数
划分问题:分割点有序类,题库:成绩最大(noip00)
分割点无序类,题库:石子合并(noi95);加分二叉树(noip03);括号序列
3、路径问题:方格取数(noip00);花店橱窗布置(ioi99)
通过这些经典模型的研究,可以从多角度了解如何进行状态的划分,然后反复理解每一个转移方程的意义,在潜移默化之中,我们便可以达到动态规划的基本要求,做到以不变应万变。
五、专题练习
在能用动态规划解决一些简单问题之后,我们需要做大量的专题练习,通过专题练习,来提高自己分析问题的能力,提高自己应用动态规划解题的能力,提高自己编程的熟练度。从而使自己真正掌握到动态规划的精髓,使动态规划成为自己制胜的法宝。
转自