动态规划入门,基础

来源:互联网 发布:c语言指针 编辑:程序博客网 时间:2024/05/19 02:30

在学习动态规划之前,请先熟悉深度优先搜索(DFS)。由于为新博客,所以烦劳先自行寻找资料学习DFS,过几日我会添加学习DFS的文章。


动态规划(DP)本是运筹学的一个分支,因此也是许多ACMer胸口永远的痛。什么是动态规划与其性质我在此不再进行说明,关于它的解释百度一下一大把。附上百度链接:点击打开链接


适用条件(看不懂可先跳过,之后我都会举例说明)

任何思想方法都有一定的局限性,超出了特定条件,它就失去了作用。同样,动态规划也并不是万能的。适用动态规划的问题必须满足最优化原理和无后效性。
1.最优化原(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2.无后效性将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。

动态规划中需要理解的3个概念(同样,我会结合例题给出说明)
状态:根据生活经验,我们知道一个某个对象处于某个时期或者某个空间,我们称为一个状态。例如小明今年15岁为一个时间上的状态,16岁又是一个新的状态。
决策:做出一个选择或者说做出一个决定,可以理解为一个决策。如:小明16岁时决定比15岁时更加努力学习计算机以取得更好的成绩。
转移:此处我们所说的转移为一个状态到另一个状态的转变,即当前状态可以由上一个状态或其他状态转变而来。如:小明16岁的成绩由15岁的成绩更上一层楼转变而来。
大家可以发现这3者是紧密相连的。。。。。。。。。。实在看不出先跳过。。。。。。

为什么动态规划如此让人头疼呢?我以从初学者过来的经验可以告诉大家,且结为下面两点。(个人理解,轻喷)

①状态?啥叫状态啊?这题状态怎么表示好啊?

②转移?这状态怎么转移?这状态还能转移?!!

不错,DP最难的两点便是状态的确定与转移。好了,废话不多说,下面我将结合趣味例题与大家分享。




例题1:

这里我们仍以大部分书上第一个例题数字三角形为例,将它改一改,相信都能看懂。

                                                     

                   

                图1                                              图2

小明非常爱玩LOL,有一天,他来到了召唤师峡谷准备大干一场,可是这一次的召唤师峡谷却是三角形的(如图1,2),小明的出生点位于图中最上面的点,为了掀翻召唤师峡谷,小明决定收集更多的炸弹,图中数字代表此地炸弹个数,且小明只能一直往下走,每次只能往左下或者右下角的一处走,正确与错误走法如下:


(确实是画得很丑。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。那你还看?)

给出你一个图,请你编写一个程序帮小明计算出小明最多可以收集多少炸弹(别担心,小明的袋子是屎做的,装得下)。


走图1时,有些同学产生了一个感觉,“这TMD用贪心不就好了?”,然后请你看看图2这时再用贪心还行吗?


相信对深度优先搜索已经有了一定认识的同学,一眼就能看出来并喊道:“这个还不简单?一遍DFS就能出来!博主你拿这么水的题来唬我?


好,那我们就先用DFS来求出结果。  为了方便,我们将所求最大值称为叶良辰值,对于某点(x,y)我们将其左下表示为(x+1,y),右下表示为(x+1,y+1),在这里我将只给出DFS的代码:点击查看代码

在代码中,我们将每一个dfs(i,j)当成(i,j)为起点的叶良辰值,这便是上面(上面你跳过的~~)所说的最优子结构。而当dfs(i+1,j)求出来后,并不会影响dfs(i,j)的求值,这便是无后效性。而每一对确定i,j都代表了一种状态。而根据dfs(x , y)=max(dfs(x + 1, y), dfs(x + 1, y + 1)) + map[x][y](怎么来的?请耐心往下看)来求出状态dfs(x,y),我们称其为状态转移方程式(原来如此哦)

经过调试我们发现结果没有错误,可tmd居然超时了,怎么回事?下面请看看这个图:


(怎么样,这图还行吧!?)

通过观察发现在求dfs(2,1)与dfs(2,2)时,都会递归进入dfs(3,2),由此造成了重复的不必要的计算,当数据一大重复计算的将占绝大部分,这便是(上面你跳过的~~)子问题的重叠性(到这里我们对5个名词的概念进行了说明了,还不懂?~~~~退坑吧!!决策将在下面给出说明)。重复计算怎么解决?

既然会重复计算,那我们为什么不再计算过dfs(3,2)后保存下这个值呢?

于是我们可以写出如下代码:点击查看代码              我们称其为  记忆化搜索 

不对啊?讲了这么久这么讲了记忆化搜索却不讲动态规划啊?  别急,记忆化搜索是利用动态规划求解的一种方式。下面我将介绍动态规划的另一种写法:递推

我们以dp[i][j]来表示以dp[i][j]为起点的叶良辰值,那么问题来了,怎么求?

第一步:确定状态dp[i][j]:表示以dp[i][j]为起点的叶良辰值

第二步:做出决策:尝试左下/右下方走(决策一定要包含所以可能,在此表现为左或右)

第三步:状态转移:根据决策来建立状态转移方程:

若往左下走则 dfs(x , y)=dfs(x + 1, y) + map[x][y],因为往左下走,所以炸弹数一定为(x + 1, y)为起点的叶良辰值再加上在(x , y)可以捡到的炸弹数map[x][y]

若往右下走则同理 dfs(x , y)=dfs(x + 1, y + 1) + map[x][y]

所以最终取最大值状态转移方程便出来了 dfs(x , y)=max(dfs(x + 1, y), dfs(x + 1, y + 1)) + map[x][y]

~~这次不偷懒了,附上完整代码:对了!在这里先给读者留下一个疑问,代码中给出的为dp为一维数组而非二维,却仍然可以求出正确结果!不信可以去POJ-3176提交试试     点击查看完整代码

此问题我们将在下一次更新时给大家讲解!也可以自己先仔细想一想!望有帮助!谢谢支持!

0 0