[笔记]动态规划(dynamic programming)

动态规划与分治方法都是通过组合子问题的解来求解原问题,区别在于:分治方法将问题划分为互不相交的子问题,递归求解子问题,再将它们的解组合起来,求出原问题的解。分治算法可能反复的求解某些公共子问题,从而使效率下降,例如用分治法求第n个斐波那契数。动态规划算法对每个子问题只求解一次,将其解保存在一个表格中,从而无需反复求解公共子问题。动态规划通常用来求解最优化问题。

应用动态规划方法求解的最优化问题应该具备两个要素

  1、最优子结构,如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质

  2、子问题重叠,如果递归算法反复求解相同的子问题,我们就称最优化问题具有重叠子问题性质。

通常采用4个步骤来设计一个动态规划算法:

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

  2、递归的定义最优解的值。

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

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

动态规划的两种实现方法

  1、带备忘录的自顶向下法:执行过程中会保存每个子问题的解。当需要一个子问题的解时,首先检查是否已经保存过此问题的解。如果是,则直接返回保存的值,从而节省计算时间;否则,按通常的方法计算这个子问题

  2、自底向上法:将子问题按规模进行排序,按由小到大的顺序进行求解。当求解某个子问题时,其子问题已经求解完毕。

  两种方法得到的算法具有相同的渐进运行时间。由于没有频繁的递归函数调用的开销,自底向上方法的时间复杂性函数通常具有更小的系数。

(注:以上来自于《算法导论·第三版》第15章动态规划)

例:

  1、(来源:LeetCode)给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:输入: [-2,1,-3,4,-1,2,1,-5,4],

      输出: 6
      解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

      来源:力扣(LeetCode)
      链接:https://leetcode-cn.com/problems/maximum-subarray
      著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

public class Solution {
    public int MaxSubArray(int[] nums) {
        if(nums.Length<1)
            return 0;
        int sum=0,Max=nums[0];
        for(int i=0;i<nums.Length;++i)
        {
            if(sum<0)
                sum=nums[i];
            else
                sum+=nums[i];
            Max=Max<sum?sum:Max;
        }
        return Max;
    }
}

思路:从第一个元素开始遍历,Max记录从第一个元素到当前元素的具有最大和的连续子数组的和(自底向上法)。

  2、(来源:LeetCode)你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

  给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

  示例 1: 输入: [1,2,3,1]

     输出: 4
  解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。
  示例 2:

  输入: [2,7,9,3,1]
  输出: 12
  解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

  来源:力扣(LeetCode)
  链接:https://leetcode-cn.com/problems/house-robber
  著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

public class Solution {
       public int Rob(int[] nums)
        {
            int dp=0;
            int sum=0;
            for (int i = 0; i < nums.Length; i++)
            {
                int tmp=dp;
                dp=Math.Max(sum+nums[i],dp);
                sum=tmp;
            }
            return dp;
        }
}

思路:自底向上法,由于每一个元素都是正整数,则长度为n的数组,最优解的结构为nums[n]+(n-2)>(n-1):nums[n]+(n-2)?(n-1),(n-2) ,(n-1)代表n-2和n-1的最优解。dp记录当前元素下的最优解,sum记录前一个元素的最优解

 

posted @ 2019-10-04 22:18  qetuo[  阅读(149)  评论(0编辑  收藏  举报