1. 程式人生 > >LeetCode-動態規劃總結(一)

LeetCode-動態規劃總結(一)

動態規劃

遞迴和動態規劃都是將原問題拆成多個子問題然後求解,他們之間最本質的區別是,動態規劃儲存了子問題的解,避免重複計算。

斐波那契數列

爬樓梯

70. Climbing Stairs (Easy)

題目描述:有 N 階樓梯,每次可以上一階或者兩階,求有多少種上樓梯的方法。

定義一個數組 dp 儲存上樓梯的方法數(為了方便討論,陣列下標從 1 開始),dp[i] 表示走到第 i 個樓梯的方法數目。

第 i 個樓梯可以從第 i-1 和 i-2 個樓梯再走一步到達,走到第 i 個樓梯的方法數為走到第 i-1 和第 i-2 個樓梯的方法數之和。


考慮到 dp[i] 只與 dp[i - 1] 和 dp[i - 2] 有關,因此可以只用兩個變數來儲存 dp[i - 1] 和 dp[i - 2],使得原來的 O(N) 空間複雜度優化為 O(1) 複雜度。

class Solution {
    public int climbStairs(int n) {
        if (n < 1) {
            return 0;
        }
        if (n == 1) {
            return 1;
        }
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        if (n <= 2) {
            return dp[n];
        }
        for
(int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } }

強盜搶劫

198. House Robber (Easy)

題目描述:搶劫一排住戶,但是不能搶鄰近的住戶,求最大搶劫量。

定義 dp 陣列用來儲存最大的搶劫量,其中 dp[i] 表示搶到第 i 個住戶時的最大搶劫量。由於不能搶劫鄰近住戶,如果搶劫了第 i -1 個住戶,那麼就不能再搶劫第 i 個住戶,所以


class Solution
{ public int rob(int[] nums) { if (null == nums || 0 == nums.length) { return 0; } int n = nums.length; if (1 == n) { return nums[0]; } int[] dp = new int[n]; dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); for (int i = 2; i < n; i++) { dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); } return dp[n - 1]; } }

強盜在環形街區搶劫

213. House Robber II (Medium)

class Solution {
    public int rob(int[] nums) {
        if (null == nums || 0 == nums.length) {
            return 0;
        }
        int n = nums.length;
        if (1 == n) {
            return nums[0];
        } else if (2 == n) {
            return Math.max(nums[0], nums[1]);
        }
        return Math.max(rob(nums, 0, n - 2), rob(nums, 1, n-1));
        
    }
    public int rob(int[] nums, int left, int right) {
        int n = right - left + 1;
        int[] dp = new int[n];
        dp[0] = nums[left];
        dp[1] = Math.max(nums[left], nums[left + 1]);
        int j = left + 2;
        for (int i = 2; i < n; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[j++]);
        }
        return dp[n - 1];
    }
}

矩陣路徑

矩陣的最小路徑和

64. Minimum Path Sum (Medium)

[[1,3,1],
 [1,5,1],
 [4,2,1]]
Given the above grid map, return 7. Because the path 1→3→1→1→1 minimizes the sum.

題目描述:求從矩陣的左上角到右下角的最小路徑和,每次只能向右和向下移動。

class Solution {
    public int minPathSum(int[][] grid) {
        if (null == grid || 0 == grid.length) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j > 0) {
                    grid[i][j] += grid[i][j - 1];
                }else if (i > 0 && j == 0) {
                    grid[i][j] += grid[i - 1][j];
                } else if (i > 0 && j > 0){
                    grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
                }
            }
        }
        return grid[m - 1][n - 1];
    }
}

矩陣的總路徑數

62. Unique Paths (Medium)

題目描述:統計從矩陣左上角到右下角的路徑總數,每次只能向右或者向下移動。

class Solution {
    public int uniquePaths(int m, int n) {
        if (0 == m || 0 == n) {
            return 0;
        }
        int[][] map = new int[m][n];
        for (int i = 0; i < m; i++) {
            map[i][0] = 1;
        }
        for (int i = 0; i < n; i++) {
            map[0][i] = 1;
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                map[i][j] = map[i - 1][j] + map[i][j - 1];
            }
        }
        return map[m - 1][n - 1];
    }
}

陣列區間

陣列中等差遞增子區間的個數

413. Arithmetic Slices (Medium)

A = [1, 2, 3, 4]
return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

dp[i] 表示以 A[i] 為結尾的等差遞增子區間的個數。

在 A[i] - A[i - 1] == A[i - 1] - A[i - 2] 的條件下,{A[i - 2], A[i - 1], A[i]} 是一個等差遞增子區間。如果 {A[i - 3], A[i - 2], A[i - 1]} 是一個等差遞增子區間,那麼 {A[i - 3], A[i - 2], A[i - 1], A[i]} 也是等差遞增子區間,dp[i] = dp[i-1] + 1。

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        if (null == A || 0 == A.length) {
            return 0;
        }
        int[] dp = new int[A.length];
        for (int i = 2; i < A.length; i++) {
            if (A[i] - A[i - 1] == A[i - 1] - A[i - 2]) {
                dp[i] = dp[i - 1] + 1;
            }
        }
        int total = 0;
        for (int k : dp) {
            total += k;
        }
        return total;
    }
}

分割整數

分割整數的最大乘積

343. Integer Break (Medim)

題目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).

class Solution {
    public int integerBreak(int n) {
        if (n <= 0) {
            return 0;
        }
        int[] dp = new int[n + 1];
        for (int i = 1; i <= n; i++) {
           for (int j = 1; j <= i - 1; j++) {
               dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j)));
           }
        }
        return dp[n];
    }
}

按平方數來分割整數

279. Perfect Squares(Medium)

題目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

class Solution {
    public int numSquares(int n) {
        if (n < 0) {
            return 0;
        }
        int[] dp = new int[n + 1];
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            dp[i] = help(i, dp);
        }
        return dp[n];
    }
    public int help(int x, int[] dp) {
        int min = Integer.MAX_VALUE;
        int flag = 1;
        while (flag <= x / flag) flag++;
        flag--;
        for (int i = flag; i >= 1; i--) {
            min = Math.min(min, x - i * i == 0 ? 1 : dp[x - i * i] + dp[i * i]);
        }
        return min;
    }
}

分割整數構成字母字串

91. Decode Ways (Medium)

題目描述:Given encoded message “12”, it could be decoded as “AB” (1 2) or “L” (12).

class Solution {
    public int numDecodings(String s) {
        if (null == s || 0 == s.length()) {
            return 0;
        }
        int n = s.length();
        int[] dp = new int[n];
        dp[0] = s.charAt(0) == '0' ? 0 : 1;
        boolean flag = true;
        for (int i = 1; i < n; i++) {
            dp[i] = dp[i - 1];
            int a = s.charAt(i) - '0';
            int b = s.charAt(i - 1) - '0';
            if (0 == a) {
                if (b > 2 || 0 == b) {
                    flag = false;
                    break;
                } else {
                    dp[i] = i - 2 >= 0 ? dp[i - 2] : 1;
                }
            } else {
                if (b != 0 && a + b * 10 <= 26) {
                    if (i - 2 >= 0) {
                        dp[i] += dp[i - 2];
                    } else {
                        dp[i]++;
                    }
                }
            }
        }
        return flag == true ? dp[n - 1] : 0;
    }
}