动态规划基础

本文介绍了动态规划的基本原理和解决最优化问题的核心思想,包括最优子结构、无后效性和子问题重叠这三个关键条件。通过数字三角形问题和矩阵最短路径问题来阐述动态规划的解题步骤,强调了动态规划与递推、贪心、分治以及搜索算法的区别。动态规划通过状态转移方程找到问题的最优解,适用于解决子问题重叠且具有最优子结构的问题。
摘要由CSDN通过智能技术生成

在已经学习动态规划很久很久之后,终于来写一篇这样总结性的文章!

(虽然学了这么久还是什么都不懂)

本文先从动态规划的基础部分开始讲起,争取清晰,透彻

(并增加一些个人的看法与理解)

开始!!!!!


简介

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。

在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖于当前面临的状态,又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线.这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策问题。在多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化的过程为动态规划方法。

——百度百科

总结:动态规划是一种求解最优化结果的方法,通过把原问题分解为相对简单的子问题求解复杂问题。同时,动态规划并不是某一种具体的算法,而是解决特定问题(常见:最值,计数等)的方法,有时也是一种**“思想”**。

引入

还是那个最经典的例题:数字三角形

给定一个 r r r 行的数字三角形( r ≤ 1000 r \leq 1000 r1000),需要找到一条从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到当前点左下方的点或右下方的点。

考虑这样一个三角形:

        7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5 

很快可以得出最长路径: 7 → 3 → 8 → 7 → 5 7 \to 3 \to 8 \to 7 \to 5 73875

从这一个路径,我们可以得到动态规划运行的本质,也是它正确性的保障:

一条最优的路径,它的每一步决策都是最优的。

我们取出路径的前四个点: 7 → 3 → 8 → 7 7 \to 3 \to 8 \to 7 7387

可以发现这条路径的值是从起点到达第四行第二个数的三条路径中数字和最大的,也就是最优的。

这道题的思路就是把求从顶部到底部的最大数字和这个母问题转化为求解从顶部到每一个点的最大数字和多个子问题,用子问题推到母问题,并保证每个子问题都一定是最优的解决方案,从而解决问题。

不难发现,用动态规划解决问题是有要求的。必须满足一些特定条件,否则无法保证正确性。

动态规划原理

动态规划解决问题的过程,需要经历多个决策阶段。每个决策阶段都对应一组状态。然后寻找一组决策序列,经过这组决策序列,能够产生最终期望求解的最优值。

而用动态规划解题,需要满足三个条件:最优子结构无后效性子问题重叠

最优子结构

最优子结构是指一个问题的最优解可以通过一系列子问题的最优解来构建。换句话说,如果一个问题的最优解可以通过其子问题的最优解推导出来,并且这些子问题在求解过程中是相互独立的,那么该问题就具有最优子结构性质。这意味着我们可以使用递归或迭代的方式来求解这个问题,将其分解为更小的子问题进行求解,然后利用子问题的最优解得到原问题的最优解。

实际上,最优子结构即是保证了子问题最优,从而使原本问题得到最优解。同时注意,在计算所有子问题时,只关心当前状态得到的最优解,不关心达到当前状态过程。换句话说,动态规划就是在子问题推进的过程中“不择手段”地走向最优解。

  • 如何证明最优子结构一定能推出母问题最优解?

    证明作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解。反证法:考虑加入某个子问题的解不是其自身的最优解,那么就可以从原问题的解中用该子问题的最优解替换掉当前的非最优解,从而得到原问题的一个更优的解,从而与原问题最优解的假设矛盾。

  • 如何利用最优子结构求解?

    最优子结构解题的不同体现在两个方面:

    1.原问题的最优解中涉及多少个子问题;

    2.确定最优解使用哪些子问题时,需要考察多少种选择。

    这就和下文会提到的构建状态相关。状态不同,也就是子问题不同,怎样用子问题组成最终答案也不同。

无后效性

这一点对于能否使用动态规划尤其重要。

即:某阶段的状态一旦确定,则此后过程的决策不再受此前各种状态及决策的影响

举例:如下图有四乘四的网格,要从左上角走的右下角,条件是每次只能向下或向右走

如下图从起点走到黑色圆圈位置 S ( 2 , 2 ) S(2,2) S(2,2) 有两种方案,但是 S ( 2 , 2 ) S(2,2) S(2,2) 接下来所做的决策不用考虑之前的决策,故是无后效性

如果把条件改为:可以往前后左右走但是不能走重复的格子,那么接下来要做的决策就需要考虑之前的决策,故此时是有后效性。(举例来源

通俗讲,就是前面提到过的只关心当前得到的结果,而不关心当前的结果得来的过程。如果要求不能重复,但是动态规划不会也不能记录到底从那些点转移来,就不能判断是否重复,从而无法保证正确性。

举例.png

子问题重叠

如果有大量的重叠子问题(即先前计算过的),我们可以用空间将这些子问题的解存储下来,需要时直接调用,避免重复求解相同的子问题,从而提升效率

动态规划在获得时间高效率的同时,可能耗费更多的空间,即时间效率高,空间耗费大。滚动数字是优化空间效率的一个办法。


三个概念看下来或许会有点懵。接下来会在介绍动态规划实现过程中再做详细解释。

基本思路

1.将原问题划分为若干阶段,每个阶段对应若干个子问题,提取这些子问题的特征(称之为状态);

2.寻找每一个状态的可能决策,或者说是各状态间的相互转移方式(用数学的语言描述就是状态转移方程)。

3.按顺序求解每一个阶段的问题。

这样看或许很抽象。接下来介绍两种动态规划解题思路。

参考资料

1.状态转移表法

一般来说,可以用动态规划解决的问题都可以用搜索来实现。

遇到动态规划的题目可以先用较为简单的搜索解决,然后据此定义状态。

两种处理思路:一是对dfs进行优化,加上记忆化,转为记忆化搜索。

记忆化搜索:对问题进行深搜的时候记录到达每一个状态的值,在以后的计算中若再次访问这个状态,直接调用存储好的值,提高效率。

另一种思路就是常规的动态规划做法。


接下来介绍状态转移表法是如何工作的:

画一个二维的状态表(若超出二维,这个方法就不太好用了)。我们根据决策的先后过程、递推关系,分阶段填充表中的每个状态。最后,将这个递推填表的过程翻译成代码。

考虑一个例题:

一个矩阵,每一个有数值,从起点 ( 0 , 0 ) (0,0) (0,0) 到终点 ( n − 1 , m − 1 ) (n-1,m-1) (n1,m1) ,只能向下或向右走。求最短路径。

考虑这个矩阵:

方格纸3.jpg

我们先用深搜的思路画出一颗递归树(一部分):

递归树.png

可以发现,相同颜色的节点就是被访问一次以上的节点,出现了子问题重叠。

如果使用记忆化搜索,可以用二维数组存下每个节点的值,二次访问时直接返回。

如果用递推做法呢?

画出一个二维状态表,表中的行、列表示棋子所在的位置,表中的值表示从起点到这个位置的最短路径:

状态转移表.PNG

根据这个推导的过程,就可以轻易写出递推代码了!

for(int i=0;i<n;i++){
	for(int j=0;j<m;j++){
		dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j];
		//dp[i][j]为到达(i,j)时的最短路径长度,a[i][j]为(i,j)处的数值
	}
}

2.状态转移方程法

这个方法适用范围更广。我们需要分析原问题如何分解成子问题,子问题如何组成原问题,就是设置状态。

还要分析子问题之间的递推关系,从而写出状态转移方程。

根据上文的例题:

状态: d p i , j dp_i,_j dpi,j 为到达 ( i , j ) (i,j) (i,j)时的最短路径长度

状态转移方程:

dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j];

状态转移方程是解决动态规划的关键。如果我们能写出状态转移方程,那动态规划问题基本上就解决一大半了,而翻译成代码非常简单。但是很多动态规划问题的状态本身就不好定义,状态转移方程也就更不好想到。

所以通过练题,了解更多设状态的方式是尤为重要的。


两种方法到这里就讲完了,注意:使用哪一种方法要根据实际情况做出选择。


动态规划的基础部分就结束了,了解动态规划运行的原理后,我们对它和另外一些相似的算法思想作比较:

算法思想比较分析

动态规划递推的关系?

学习了比较长时间的动态规划和递推,一直对两者之间的关系存有疑惑。

初步结论(个人观点)

递推是一种计算机算法,通常用来将一个递推函数转化成一个线性或多项式时间的求解程序。

动态规划是一个运筹学算法,通常用来将一个满足特定条件的决策最优化问题转化为一个递推函数。

递推编码形式只是用来解决动态规划问题的一种方法。如果问题本身就是递推公式(如斐波那契数列),就不应该称作是动态规划。

同时,两者区分还有一个特点:递推往往有严格的转移,每一步的转移是确定的,例如通常计算一些有规律的序列,规律是确定的,严格按照得到的递推式进行转移。而动态规划的每个状态会面临多个不同的决策,不同的决策会导致进入不同的状态。所以二者是有区别的。

综上所述:递推是一种解决问题的具体实现方法,而动态规划是将一个问题转化为递推方式解决的算法思路

贪心、分治、搜索和动态规划

如果对这四种算法思想分类,贪心、搜索、动态规划可以归为一类,而分治单独可以作为一类。


贪心,搜索和动态规划可以抽象为多阶段决策模型,而它们的不同之处在于解决的问题满足不同的条件

贪心:最优子结构无后效性贪心选择性

贪心选择性:通过局部最优的选择,能产生全局的最优选择。

通过贪心选择性,我们可以发现,贪心实际上是一种特殊的动态规划,但是满足贪心选择性后,就不需要复杂决策,当前最优一定构成全局最优

搜索:搜索是其中可做范围最广的,它无需满足无后效性,只需要回溯就可以解决。

但是同样也造成搜索的复杂度极高,是指数级别的,限制了它的应用范围。

动态规划满足的条件本文已经解释,但同样,动态规划的时间和空间也会在一定程度上受到限制。


而分治就大不同了。分治的原理是把原问题分解成子问题,再用子问题推回原问题,而且最重要的是子问题不重叠

结合下图理解(图片来源):

分治.PNG

与上文的递归树相比,可以看到,分治是“分”后“治”再“合”,所以不允许有子问题重叠,否则无法保证正确性。


这一篇动态规划基础就结束了!

后面会写新的文章对不同种类的动态规划再做总结。

参考资料:

博客1 博客2 博客3 oi-wiki 百度百科 《算法竞赛(上)》 罗勇军 郭卫斌著

The end.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值