動態規劃法(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.this

代碼實現

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 trianglecode

[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).get

代碼實現

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];
   }
相關文章
相關標籤/搜索