动态规划法(LeetCode经典案列解析)

超大只乌龟

今天来总结几道LeetCode上有关动态规划的经典题目:

  • Best Time to Buy and Sell Stock(买卖股票的最佳时机)
  • House Robber(抢劫最大金额)
  • Integer Break(整数切割)
  • Minimum Path Sum(最小路径和)
  • Triangle(三角形)

Best Time to Buy and Sell Stock(买卖股票的最佳时机)

题目:如果你最多只获准完成一项交易(即,买一股,卖一股),设计一个算法来寻找最大的利润

Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.

Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

代码实现

public int maxProfit(int[] prices) {
    
    int ans = 0;
    if (prices.length == 0) {
        return ans;
    }
    
    int bought = prices[0];
    for (int i = 1; i < prices.length; i++) {
        if (prices[i] > bought) {
            if (ans < (prices[i] - bought)) {
                ans = prices[i] - bought;//找到最大利润
            }
        } else {
            bought = prices[i];//找到最小成本
        }
    }
    return ans;
}

House Robber(抢劫最大金额)

题目:给定一个整数数组表示一系列连续的屋子里面放有的金钱,在不能连续进入相邻屋子抢钱的情况下求能抢到的最大金额。
Example :
Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

解题思路

  1. 只有1个房屋nums[0],最大收益为dp[0] = nums[0];
  2. 有2个房屋nums[0], nums[1], 不能同时取,最大收益为dp[1] = max(nums[0], nums[1]);
  3. 有3个房屋,有两种取法,取nums[1],或者取nums[0]和nums[2].即 dp[2] = max(nums[1], nums[0] + nums[2]);
  4. 故可推测出动态转换方程为:dp[i] = max(nums[i] + dp[i-2], dp[i-1]);

代码实现

public static int rob(int[] nums) {

    if (nums == null || nums.length == 0) throw new RuntimeException("不合法输入");
    if (nums.length == 1) return nums[0];
    if (nums.length == 2) return nums[1] > nums[0] ? nums[1] : nums[0];

    int n = nums.length;
    int[] rob = new int[n];
    //初始化rob[0]和rob[1]
    rob[0] = nums[0];
    rob[1] = nums[1] > nums[0] ? nums[1] : nums[0];
    //方程式:dp[i] = max(nums[i] + dp[i-2], dp[i-1])
    for (int i = 2; i < n; i++) {
        rob[i] = rob[i - 1] > rob[i - 2] + nums[i] ? rob[i - 1] : rob[i - 2] + nums[i];
    }
    return rob[n - 1];
    }
}

Integer Break(整数切割)

题目:将一个数分割成若干个正整数的和,求这若干个正整数乘积最大值
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

代码实现

class Solution {
    //动态规划:自底向上
    public static int integerBreak(int n) {
        //条件判断
        if (n <= 0) 
             throw new RuntimeException("不合法输入");
        int[] dp = new int[n + 1];
        Arrays.fill(dp, -1);
        dp[1] = 1;
        //计算dp[2]~dp[n]的值
        for (int i = 2; i <= n; i++) {
            //j表示有1~n-1种切法(第一段切割成[1,n-1],[2,n-2]...[n-1,1])
            for (int j = 1; j < i - 1; j++) {
                dp[i] = max3(dp[i], j * (i - j), j * dp[i - j]);
            }
        }
        return dp[n];
    }
    //返回三个数中的最大值
    private static int max3(int a, int b, int c) {
        return Math.max(Math.max(a, b), c);
    }
}

Minimum Path Sum(最小路径和)

题目:给定一个二维数组,从左上角出发,每次只能向下或向右移动,找到从左上角到右下角的最短路径和。
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

解题思路

1.假如我们就在最右下角的格子(也可以想象成网格只有一个格子),那么最短路径和就是格子中的值
2.假如我们在最后一行,假如是arow-1,那么从这个点出发到最右下角的最小路径和就是它本身加上它右边的格子到最右下角的最小路径和
3.最后一列和最后一行是同理的
4.一个普通的位置,它到最右下角的最小路径和是多少呢,是它右边一个位置和它下面一个位置的最小路径和中最小的那个加上它自身的值

代码实现

    public static int dpMinPath(int[][] a) {
        //条件判断
        if (a == null || a.length == 0 || a[0].length == 0) {
            throw new RuntimeException("不合法输入!");
        }
         int row = a.length;
        int col = a[0].length;
        
        //dp[i][j]表示a[i][j]到右下角的最短路径和
        int dp[][] = new int[row][col];
        dp[0][0] = a[0][0];
        //填充第一列
        for (int i = 1; i < row; i++) {
            dp[i][0] = a[i][0] + dp[i - 1][0];
        }
        //填充第一行
        for (int i = 1; i < col; i++) {
            dp[0][i] = a[0][i] + dp[0][i - 1];
        }
        //其它
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = a[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[row - 1][col - 1];
    }
}

Triangle(三角形)

题目:给定一个三角形,找到从上到下的最小路径总和。 您可以移动到下面一行中相邻数字的每一步。
For example, given the following triangle

    [2]
   [3,4]
  [6,5,7]
 [4,1,8,3]

The minimum path sum from top to bottom is 11 ( 2 + 3 + 5 + 1 = 11).

代码实现

    public static int minimumTotal(List<List<Integer>> triangle) {
        //条件判断
        if (triangle == null || triangle.size() == 0)  return 0;
        if (triangle.size() == 1) return triangle.get(0).get(0);

        int[] dp = new int[triangle.size()];
        //从最后一行向上遍历
        for (int i = triangle.size() - 1; i >= 0; i--) {
            for (int j = 0; j < triangle.get(i).size(); j++) {
                //最后一行的dp[j]是对应的数值
                if (i == triangle.size() - 1) {
                    dp[j] = triangle.get(i).get(j);
                } else {
                    //从倒数第二行开始,dp[j]=左下dp值和右下dp值中的较小的那个+自身的值
                    dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
                }
            }
        }
        return dp[0];
   }
阅读 275

区区码农

701 声望
995 粉丝
0 条评论

区区码农

701 声望
995 粉丝
宣传栏