动态规划

概述

正向递归,记录每一次执行的结果,用于下一次计算

可以用来解决的问题:

  • 需要大量遍历寻找最优解 框架公式
  • 单个数组或者字符串要用动态规划时,可以把动态规划 dp[i] 定义为 nums[0:i] 中想要求的结果
  • 当两个数组或者字符串要用动态规划时,可以把动态规划定义成两维的 dp[i][j] ,其含义是在 A[0:i] 与 B[0:j] 之间匹配得到的想要的结果
  • 1.状态定义
  • 2.状态转移方程
  • 3.状态的初始化
  • 4.遍历方向与范围
  • 5.最终返回结果

最长递增子序列

Q:给你一个整数数组 nums ,找到其中最长严格递增子序列的长度

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4

关键点

  • 动态规划:dp[i]=max(dp[j])+1,其中0≤j<i且num[j]<num[i]
public int lengthOfLIS(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int[] dp = new int[nums.length];
    dp[0] = 1;
    int maxans = 1;
    for (int i = 1; i < nums.length; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        maxans = Math.max(maxans, dp[i]);
    }
    return maxans;
}

零钱兑换 [真题]

Q:给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

关键点

  • 动态规划
    //amount=11,可以从dp[10],dp[9],dp[6]3个地方到达
    public int coinChange(int[] coins, int amount) {
        if (amount == 0) return 0;
        //(1)初始化DP table,零钱为0则硬币0个
        int[] dp = new int[amount + 1];
        dp[0] = 0;

        //(2)遍历每种【状态】即金额,【选择】即保存最少硬币数量
        for (int amounttt = 1; amounttt <= amount; amounttt++) {
            //(1.1)初始化DP table。初始为最大硬币数量即amount个,因为最小面额为1元,那么+1即为无法到达的硬币个数。当然取(amount+1,Integer.MAX_VALUE - 1)(ps:后面存在1+dp[balance]计算)范围都可
            dp[amounttt] = amount + 1;
            //(3)遍历每种面额的硬币
            for (int coin : coins) {
                //(3.1)剩余金额balance,为当前amount-某额度硬币
                int balance = amounttt - coin;
                //(3.2)剩余额度>=0,说明该种硬币的组合可行
                if (balance >= 0) {
                    //(3.3)说明amount额度足以减去某额度硬币。balance<amounttt,balance于前面已经计算保存好【状态】,+1为加上当前硬币
                    //例如1:coins[1,2,5],amounttt=4
                    //①balance=4-1,此时dp[4]=min(初始值,1+dp[3]),而dp[3]由1元+2元组合为2枚。保存最小值dp[4]=3
                    //②balance=4-2,此时dp[4]=min(初始值,1+dp[2]),而dp[2]由2元组合为1枚。保存最小值dp[4]=2
                    //③balance=4-5,不足以减去该面额
                    //例如2:coins[3],amounttt=5。dp则为[0,max,max,1,max,max,max],毕竟max<max+1
                    dp[amounttt] = Math.min(dp[amounttt], 1 + dp[balance]);
                }
            }
        }

        //(4)如amount等于初始值,说明在给定种类的硬币中无法组合成amount元。比如coin[2,3] amount=1,4...
        return dp[amount] == (amount + 1) ? -1 : dp[amount];
    }

最长公共子序列 [真题]

Q:给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 

关键点

  • 动态规划
public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    //初始化为+1是为了当dp[0][0]时可以表示的为空字符串和另外一个字符串的匹配
    int[][] dp = new int[m + 1][n + 1];
    for (int i = 1; i <= m; i++) {
        char c1 = text1.charAt(i - 1);
        for (int j = 1; j <= n; j++) {
            char c2 = text2.charAt(j - 1);
            if (c1 == c2) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[m][n];
}

最大子序和 [真题]

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

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

关键点

  • 动态规划
public int maxSubArray(int[] nums) {
    int pre = 0, maxAns = nums[0];
    for (int x : nums) {
        pre = Math.max(pre + x, x);
        maxAns = Math.max(maxAns, pre);
    }
    return maxAns;
}

买卖股票 [真题]

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

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

关键点

  • 动态规划
    public int maxProfit(int prices[]) {
        int minprice = Integer.MAX_VALUE;
        int maxprofit = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minprice) {
                minprice = prices[i];
            } else if (prices[i] - minprice > maxprofit) {
                maxprofit = prices[i] - minprice;
            }
        }
        return maxprofit;
    }