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;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值