动态规划概述

动态规划的基本思想

如果各个子问题不是独立的,不同的子问题的个数只是多项式量级,如果我们能够保存已经解决的子问题的答案,而在需要的时候再找出已求得的答案,这样就可以避免大量的重复计算。由此而来的基本思路是,用一个表记录所有已解决的子问题的答案,不管该问题以后是否被用到,只要它被计算过,就将其结果填入表中。

动态规划的基本步骤

  1. 找出最优解的性质,并刻画其结构特征。

  2. 递归地定义最优值。

  3. 以自底向上的方式计算出最优值。

  4. 根据计算最优值时得到的信息,构造一个最优解。

其中123步是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤4可以省去。若需要求出问题的一个最优解,则必须执行步骤4。此时,在步骤中计算最优值时,通常需记录更多的信息,以便在步骤中,根据所记录的信息,快速构造出一个最优解。

动态规划问题的特征

1、最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。

2、重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。

动态规划的形式

递推

记忆化搜索

状态与状态转移

最优化原理与最优子结构

决策与无后效性

 

例题:

老王想在未来的n年内每年都持有电脑,m(y, z)表示第y年到第z年的电脑维护费用,其中y的范围为[1, n],z的范围为[y, n],c表示买一台新的电脑的固定费用。 给定矩阵m,固定费用c,求在未来n年都有电脑的最少花费。 考虑第 i 年是否要换电脑,换和不换是不一样的决策,那么我们定义一个二元组(a, b),其中 a < b,它表示了第a年和第b年都要换电脑(第a年和第b年之间不再换电脑),如果假设我们到第a年为止换电脑的最优方案已经确定,那么第a年以前如何换电脑的一些列步骤变得不再重要,因为它并不会影响第b年的情况,这就是无后效性。 更加具体得,令d[i]表示在第i年买了一台电脑的最小花费(由于这台电脑能用多久不确定,所以第i年的维护费用暂时不计在这里面),如果上一次更换电脑的时间在第j年,那么第j年更换电脑到第i年之前的总开销就是c + m(j, i-1),于是有状态转移方程: d[i] = min{ d[j] + m(j, i-1) | 1 <= j < i } + c 这里的d[i]并不是最后问题的解,因为它漏算了第i年到第n年的维护费用,所以最后问题的答案: ans = min{ d[i] + m(i, n) | 1 <= i < n } 我们发现两个方程看起来很类似,其实是可以合并的,我们可以假设第n+1年必须换电脑,并且第n+1年换电脑的费用为0,那么整个阶段的状态转移方程就是: d[i] = min{ d[j] + m(j, i-1) | 1 <= j < i } + w(i) 其中w(i) = (i==n+1)?0:c; d[n+1]就是我们需要求的最小费用了。

 

动态规划的模型

线性模型

在一个夜黑风高的晚上,有n(n <= 50)个小朋友在桥的这边,现在他们需要过桥,但是由于桥很窄,每次只允许不大于两人通过,他们只有一个手电筒,所以每次过桥的两个人需要把手电筒带回来,i号小朋友过桥的时间为T[i],两个人过桥的总时间为二者中时间长者。问所有小朋友过桥的总时间最短是多少。 我们先将所有人按花费时间递增进行排序,假设前i个人过河花费的最少时间为opt[i],那么考虑前i-1个人过河的情况,即河这边还有1个人,河那边有i-1个人,并且这时候手电筒肯定在对岸,所以 opt[i] = opt[i-1] + a[1] + a[i] (让花费时间最少的人把手电筒送过来,然后和第i个人一起过河) 如果河这边还有两个人,一个是第i号,另外一个无所谓,河那边有i-2个人,并且手电筒肯定在对岸,所以 opt[i] = opt[i-2] + a[1] + a[i] + 2a[2] (让花费时间最少的人把电筒送过来,然后第i个人和另外一个人一起过河,由于花费时间最少的人在这边,所以下一次送手电筒过来的一定是花费次少的,送过来后花费最少的和花费次少的一起过河,解决问题) 所以 opt[i] = min{opt[i-1] + a[1] + a[i] , opt[i-2] + a[1] + a[i] + 2a[2] }

 

动态规划的简单使用

缓存与动态规划

例子:有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?

方法一:递归

1 int  solution(int n){
2     if(n>0 && n<=2) return n;
3     return solution(n-1) + solution(n-2);
4 }

方法二:DP记录避免重复计算数据

1 int dp[11];
2 int  solution(int n){
3     if(n>0 && n<=2) return n;
4     if(dp[n]!=0) return dp[n];
5     dp[n] = solution(n-1) + solution(n-2);
6     return  dp[n];
7 }

 

参考:https://blog.csdn.net/WhereIsHeroFrom/article/details/78922215

 

posted @ 2020-04-27 21:54  Jason66661010  阅读(306)  评论(0编辑  收藏  举报