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

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

我们通常按如下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<n;i++)
        {
            result[i]=result[i-1]+result[i-2];
        }
        return result[n-1];
    }
};

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

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

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

class Solution {
public:
    int minPathSum(vector<vector<int> > &grid) {
        int m=grid.size();
        int n=grid[0].size();
        int **f=new int *[m];
        for(int i=0;i<m;i++)
            f[i]=new int [n];		//声明二维动态数组
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                f[i][j]=0;		//二维动态数组全部置0
        f[0][0]=grid[0][0];
        for(int i=1;i<m;i++)
            f[i][0]=f[i-1][0]+grid[i][0];       // 第1列只能从上向下移动
        for(int j=0;j<n;j++)
            f[0][j]=f[0][j-1]+grid[0][j];	//第1行只能从左向右移动
        for(int i=1;i<m;i++)
            for(int j=1;j<n;j++)
            {
                if(f[i-1][j]<f[i][j-1])
                    f[i][j]=f[i-1][j]+grid[i][j];
                else
                    f[i][j]=f[i][j-1]+grid[i][j];	//取f[i][j-1]和f[i-1][j]的较小值
            }
        return f[m-1][n-1];
    }
};
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;i<n;i++)
        {
            sum=sum<0?A[i]:sum+A[i];
            max=sum>max?sum:max;
        }
        return max;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值