【算法学习笔记】-动态规划

动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。

我们通常按如下4个步骤来设计一个动态规划算法:

1.刻画一个最优解的结构特征。

2.递归地定义最优解的值。

3.计算最优解的值,通常是用自底向上的方法。

4.利用计算出来的信息构造一个最优解。

步骤1~3是动态规划算法求解问题的基础。如果我们仅仅需要一个最优解的值,而非解本身,可以忽略步骤4。如果确实要做步骤4,有时就需要在执行步骤3的过程中维护一些额外信息,以便用来构造一个最优解。

自底向上方法:这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它时,它的所有前提子问题都已求解完成。

下面介绍几道线性规划的例题。

1.最经典的动态规划问题:爬楼梯问题

题目:有n阶楼梯,每次可以爬一阶楼梯或者两阶楼梯,则爬完n阶楼梯总共有多少种方法。

解析:爬n阶楼梯的方法数等于爬n-1阶楼梯方法数与爬n-2阶楼梯方法数的和。当n=1,则只有1种方法;当n=2时有2种方法。据此我们可以归纳出公式如下:

         f(i)=f(i-1)+f(i-2)     3<=i<=n

         f(i)=2 i=2

         f(i)=1  i=1

         采用自底向上的方法,从i=3开始向上递归求解即可。代码如下:

class Solution {
public:
    int climbStairs(int n) {
        int *result=new int[n];
        result[0]=1;
        result[1]=2;
        for(int i=2;i

2.m*n阶宫格求最优解问题

题目:给定m*n阶宫格,每个宫格内都填充了非负数。从宫格的左上角移动到右下角,只允许向下或者向右移动。找到一条路径,使得经过该路径的所有数字之和最小。

解析:到第i行第j列宫格的最小和为到第i行第j-1列宫格的最小和与到第i-1行第j列宫格的最小和的较小值。代码如下:

class Solution {
public:
    int minPathSum(vector > &grid) {
        int m=grid.size();
        int n=grid[0].size();
        int **f=new int *[m];
        for(int i=0;i

3.求数组中连续子数组的最大和

举例,给定如下数组:[-2,1,-3,4,-1,2,1,-5,4],则连续子数组[4,-1,2,1]具有最大的和,为6。

解析:已知了前k个元素的最大子序列和为maxSub(已经被记录下来了),以及一个临时和sum,如果添加了第k+1这个元素,由于是连续子序列这个限制,所以如果k+1这个元素之前的和是小于0的,那么对于增大k+1这个元素从而去组成最大子序列是没有贡献的,所以可以把sum 置0。代码如下:

class Solution {
public:
    int maxSubArray(int A[], int n) {
        int sum=A[0],max=A[0];
        for(int i=1;imax?sum:max;
        }
        return max;
    }
};

你可能感兴趣的:(算法与数据结构,数据结构与算法学习笔记)