个人动态规划小结

动态规划(dp)通常用于求解一些具有“最优”、“最差”性质的问题。

基本思想:类似于分治法,将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

动态规划有三个要素:

       (1)问题阶段

  (2)阶段的状态

  (3)前后递推关系。

递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处。

  确定了动态规划的这三要素,整个求解过程就可以用一个最优决策表来描述,最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。

根据上面的三要素,满足如下三种情况的问题都可以选择使用动态规划进行求解:

(1) 具有最优子结构:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优子结构。

 (2) 无后效性:某状态以后的过程不会影响以前的状态,只与当前状态有关

(3) 重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。

动态规划一般有固定的求解顺序:

1:找出最优解的性质,并描述其结构特征(一般可以用反证法证明具有最优子结构)

2:递归定义最优值(明确属于哪一类问题)

3:以自底向上的方式计算最优值(明确dp数组每一位代表的含义,明确最终解要如何得到)

4:根据计算最优值时得到的信息构造出最优解(回溯法,大多一个需要辅助dp数组,在dp时记录相应的值)

以背包问题为例,实现动态规划的流程

i.如果使用暴力求解的方式来编程,共n件物品,每个物品的状态为选取或者不选,算法复杂度为O(2n),显然很差。利用反证法可以证明这个问题符合最优子结构。

设(x1,x2,x3…xn)是0-1背包问题的一个最优解。

如果x1=1,那么(x2,x3,x4,x5…xn)是以下子问题的最优解:
在这里插入图片描述
如果x1 =0:则 (x2, …, xn) 是以下子问题的最优解
在这里插入图片描述

用一个二维数组 m [ ] [ ]来记录在某个背包容量下,某个物品数下所对应的最大包内价值。

ii.分析问题可以列出状态转移方程为:

å¨è¿éæå¥å¾çæè¿°

表示在j背包容量下是否选择放入i物品,选择能使总价值最大的方法。

iii.根据实际数据按照递推式向二维数组中填入值,目标是求出矩阵右下角的那个值。

iv.从右下角(最后一个物品)开始,如果这个值和它上方的值相同,则舍弃,继续看前一个物品。如果值不同,则横坐标要减去这个物品的重量,继续看下一个物品,如此循环即可构造最优方式。

函数代码如下(c):

int getmax(int x,int y){
	return x>y?x:y;
}
void getBest(int v[],int w[]){
	int i,j;
	for(i=1;i<=N;i++){
		for(j=1;j<=W;j++){
			if(w[i-1]>j)
				m[i][j]=m[i-1][j];
			else{
				m[i][j]=getmax(m[i-1][j-w[i-1]]+v[i-1],m[i-1][j]);
			} 
		}
	}
}
void Traceback(int w[]){
	int i,j=W;
	for(i=N;i>0;i--){
		if(m[i][j]==m[i-1][j])
		continue;
		else{
			j=j-w[i-1];
			res[i]=1;
		}
	}
}

几种dp经典问题

1.矩阵连乘

n个矩阵每个矩阵和它之后的矩阵都可乘,确定连乘次序使连乘次数最小(等价于加括号)。

要点在于需要一层额外的循环来实现对不同长度的矩阵最优乘法的记录,在递推时需要从最短长度2开始逐渐拓展,不断寻找最优分割点k,而最终结果需要存放在右上角,代表“从第一个元素开始到最后一个元素的最优值”。

这个问题可以拓展出多边形的剖分三角形问题、钢条切割问题等,这类问题一般对于每一“段”长度没有要求,这一点要和商店选址问题区分(需要一个固定的最小间隔,属于背包问题)。

2.0-1背包问题

n个物品,各有一件,重量不同,价值不同,装入固定大小背包,求如何装重量最大。

这类问题的思路一般就是现根据“背包”的“重量”选择是否放入“物品”,再根据是否放入选择可以产生最大“价值”的方法。

由此可以延伸出完全背包问题(物品无限,比较典型的是硬币找零问题)、多背包问题(物品件数各不相等,比较典型的就是双机调度问题)等。 

 3.最长公共子序列问题

求两个字符串最长的一条公共子序列的长度。

这类问题通常和输入有关,如果是要求两个字符串中间的关系则一般要用二维dp数组来进行求解,如果只有一个字符串(例如求最大子段和)一般一维dp数组就可以解决。这类问题有比较多的衍生问题,例如最长递增子序列,最长回文序列等等。

另外,值得注意的是动态规划要和贪心有所区分:

1.贪心算法中,作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留。

2.贪心法正确的条件是:每一步的最优解一定包含上一步的最优解。

而在动态规划算法中,全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解。动态规划的关键是状态转移方程,即如何由以求出的局部最优解来推导全局最优解。也就是说,把一个复杂问题分解成一块一块的小问题,每一个问题得到最优解,再从这些最优解中获取更优的答案。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值