leetcode | 分类整理4(动态规划)

递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。

重点都是要找到转移方程

Fibonacci:

在上述方法中,我们使用 dp 数组,其中 dp[i]=dp[i-1]+dp[i-2]。可以很容易通过分析得出 dp[i]其实就是第 ii个斐波那契数。
                                                                              Fib(n)=Fib(n−1)+Fib(n−2)

重点是确定pre1和pre2,以及最后要返回哪个

70. 爬楼梯(e)

198. 打家劫舍(e)

213. 打家劫舍2(m)

强盗在环形区抢劫

环状排列意味着第一个房子和最后一个房子中只能选择一个偷窃,因此可以把此环状排列房间问题约化为两个单排排列房间子问题:

在不偷窃第一个房子的情况下(即nums[1:]),最大金额是 p_1
在不偷窃最后一个房子的情况下(即 nums[:n−1]),最大金额是 p_2
综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2)。

public int rob(int[] nums) {
        if (nums == null || nums.length <= 0) {
            return 0;
        }
        if (nums.length == 1) {
            return nums[0];
        }

        int n = nums.length;

        return Math.max(robHelper(nums,0,n-1),robHelper(nums,1,n));
    }

    private int robHelper(int[] nums, int start, int end) {
        int pre2 = 0 , pre1 = nums[start];
        for (int i = start + 1; i < end; i++) {
            int cur = Math.max(pre2+nums[i],pre1);
            pre2 = pre1;
            pre1 = cur;
        }
        return pre1;
    }
}

4. 信件错排

题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。

定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况:

  • i==k,交换 i 和 j 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-2] 种错误装信方式。
  • i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-1] 种错误装信方式。

综上所述,错误装信数量方式数量为:

矩阵路径:

64. 最小路径和(m)

重点:一维动态规划,dp 数组的大小和行大小相同。这是因为对于某个固定状态,只需要考虑下方和右侧的节点。

题目要求,只能向右或向下走,换句话说,当前单元格 (i,j)(i,j) 只能从左方单元格 (i-1,j)(i−1,j) 或上方单元格 (i,j-1)(i,j−1) 走到,因此只需要考虑矩阵左边界和上边界。

public int minPathSum(int[][] grid) {
        if (grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int m = grid.length, n = grid[0].length;
        int[] dp = new int[n];
        for (int i = 0 ;i < m; i++) {
            for (int j = 0 ;j < n;j ++) {
                if (i == 0 && j == 0) {
                    dp[j] = grid[i][j];
                }
//                第一行,只能从左边来
                else if (i == 0) {
                    dp[j] = dp[j-1] + grid[i][j];
                }
//                第一列,只能从上边来
                else if (j == 0) {
                    dp[j] = dp[j] + grid[i][j];
                }
                else {
                    dp[j] = Math.min(dp[j],dp[j-1])+grid[i][j];
                }
            }
        }

        return dp[n-1];
    }

62. 不同路径(m)

从左边来的dp[i-1][j]+从上边来的dp[i][j-1]
public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    for (int i = 0; i < n; i++) dp[0][i] = 1;
    for (int i = 0; i < m; i++) dp[i][0] = 1;
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }
    return dp[m - 1][n - 1];
}

使用二维数组的时候,dp[i][j] = dp[i-1][j] + dp[i][j-1] ,每一个格子的数据等于上面一个格子加左边格子的数据。可以想象一下,计算一行数据的时候,直接把上面一行的数据搬下来,然后每个格子就等于前一个格子的数据加上当前格子的数据。

public int uniquePaths(int m, int n) {
    int[] cur = new int[n];
    Arrays.fill(cur,1);
    for (int i = 1; i < m;i++){
        for (int j = 1; j < n; j++){
            cur[j] += cur[j-1] ;
        }
    }
    return cur[n-1];
}
发布了53 篇原创文章 · 获赞 5 · 访问量 1505

猜你喜欢

转载自blog.csdn.net/zhicheshu4749/article/details/104324139