• LeetCode动态规划的解题思路


    动态规划

    动态规划,其实就是找规律,总结公式/方程。
    动态规划,类似于数学归纳法。
    关键的思想在「自底向上」和「空间换时间」。
    动态规划,可以使用一维数组,有时也会用到二维数组。

    应用场景

    “动态规划”可以用于子序列、最大/小值问题的求解。

    一维数组dp[i] 的动态规划的几个步骤:

    • 确定数组dp[i]的下标i以及dp[i]值的含义,比如经典的LeetCode70爬楼梯, 爬到第i层楼梯,有dp[i]种方法;
    • 确定动态规划的状态转移方程(递推公式)。比如,爬楼梯的公式:dp[i] = dp[i-1] + dp[i-2];
    • dp数组的初始化:初始化值,dp[0]的值是多少 , dp[1]的值又是多少;
    • 确定遍历顺序:分析递推顺序应该是从前往后,还是从后往前。还有就是,要从哪一个下标开始遍历;

    LeetCode70. 爬楼梯

    假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
    每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

    public int climbStairs(int n) {
    	if (n <= 0) {
    	    return 0;
    	}
            //dp数组的初始化
            //爬到第i层楼梯,有dp[i]种方法;
    	int[] dp = new int[n + 2];
            //dp数组的初始化
    	dp[0] = 0;
    	dp[1] = 1;
    	dp[2] = 2;
    	for (int i = 3; i <= n; i++) {
                    //确定动态规划的状态转移方程(递推公式)
    		dp[i] = dp[i - 1] + dp[i - 2];
    	}
    	return dp[n];
    }
    

    LeetCode53. 最大子数组和

    • 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
      子数组 是数组中的一个连续部分。

    示例 1:
    输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
    输出:6
    解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

    class Solution {
        public int maxSubArray(int[] nums) {
            int length=nums.length;
            int[] dp=new int[length];
            //初始值
            dp[0]=nums[0];
            int maxSum=nums[0];
            for(int i=1;i<length;i++) {
                //dp[i]表示长度为i的子数组的最大值
                //由于子数组是以nums[i]结尾,
                //如果dp[i-1]是正数,那么dp[i]最大值是dp[i-1]+nums[i]
                //如果dp[i-1]是负数,那么dp[i]最大值是nums[i]
                //状态转移方程如下:
                dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
                //找出不同的数组元素结尾的最大值。
                maxSum=Math.max(dp[i],maxSum);
            }
            return maxSum;
        }
    }
    
    

    用二维数组的动态规划:

    二维数组的动态规划,跟一维数组的动态规划,基本是一样的。

    • 设定状态。
      二维 dp 问题,可以使用二维数组 dp[i][j],第一维的下标i可以表示A事物的状态,第二维的下标j可以表示B事物的状态。
      比如LeetCode122的买卖股票,题中有两个状态,一个是天数,一个是是否持有股票,
      定义dp[i][0]表示第 i天交易完后手里没有股票的最大利润,
      dp[i][1] 表示第 i天交易完后手里持有一支股票的最大利润。
    • 思考状态转移方程(也就是公式)。
      找规律,找出 dp[i][j]是怎么由dp[i-1][j]、 dp[i-1][j-1] 推导得到的
    • 考虑初始值。
      也就是 dp[0][0] 、 dp[0][1] 之类的初始值。
    • 考虑输出。
      求出 dp[len - 1][j] (也可能是其他的如dp[len - 1][j]) 的值。

    二维数组动态规划:122. 买卖股票的最佳时机 II

    给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

    在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

    返回 你能获得的 最大 利润 。

    链接: https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/

    解答:定义状态 dp[i][0] 表示第 i 天交易完后手里没有股票的最大利润,dp[i][1] 表示第 i 天交易完后手里持有一支股票的最大利润(i 从 0 开始)。

    考虑 dp[i][0] 的转移方程,如果这一天交易完后手里没有股票,那么可能的转移状态为前一天已经没有股票,即 dp[i−1][0],或者前一天结束的时候手里持有一支股票,即 dp[i−1][1],这时候我们要将其卖出,并获得 prices[i]的收益。
    dp[i][0]=max{dp[i−1][0],dp[i−1][1]+prices[i]}

    对于dp[i][1],按照同样的方式考虑转移状态,那么可能的转移状态为前一天已经持有一支股票,即 dp[i−1][1],或者前一天结束时还没有股票,即 dp[i−1][0],这时候我们要将其买入,并减少 prices[i] 的收益。可以列出如下的转移方程:
    dp[i][1]=max⁡{dp[i−1][1],dp[i−1][0]−prices[i]}

        public int maxProfit(int[] prices) {
            if (prices==null) {
                return 0;
            }
            int length = prices.length;
            //定义状态 dp[i][0]  表示第 i 天交易完后手里没有股票的最大利润,
            //dp[i][1] 表示第 i 天交易完后手里持有一支股票的最大利润(i 从 0 开始)。
            int[][] dp = new int[length][2];
            dp[0][0]=0;
            dp[0][1]=-prices[0];
            for (int i=1;i<length;i++) {
                dp[i][0] =  Math.max (dp[i-1][0], dp[i-1][1]+prices[i]);
                dp[i][1] =  Math.max (dp[i-1][1], dp[i-1][0]-prices[i]);
            }
            return dp[length-1][0];
        }
    
    

    常见题目

    动态规划常见题:LeetCode70,LeetCode121,LeetCode122,LeetCode5

    参考资料:

    《labuladong的算法小抄》
    https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/bao-li-mei-ju-dong-tai-gui-hua-chai-fen-si-xiang-b/

  • 相关阅读:
    关于闭包的一些知识
    浏览器解析JavaScript原理(1)
    函数作用域及函数表达式
    jquery
    前端常用插件
    Git及GitHub
    angular框架
    express
    ES6基础
    Node.js相关总结
  • 原文地址:https://www.cnblogs.com/expiator/p/16403345.html
Copyright © 2020-2023  润新知