美文网首页LeetCode
198(213)-打家劫舍Ⅰ、Ⅱ - 比较典型的DP

198(213)-打家劫舍Ⅰ、Ⅱ - 比较典型的DP

作者: 华雨欣 | 来源:发表于2020-03-20 22:13 被阅读0次

算是比较典型的DP(动态规划),虽然我还没开始刷DP的专题,不过跟着每日一题也做了几道,本着输出的原则把思路、算法写下来,如有不对的地方还请指出。

题目

核心思路

作为典型的DP问题,主要应该分为4步:划分子问题、状态转移、自顶向下或自底向上、优化空间。我们来分步进行解题。

划分子问题

由于题目中要对n个房间进行抢劫,显而易见,抢劫一个房间即为最小子问题,对于一个独立的房间,他有两种状态:偷/不偷,只要有偷1个房间,递推到偷2、3、4......即可解决这类问题。

状态转移

这是比较重要的一步,我们可以这样想:对于第i个房间,有两种选择:

  1. 偷这个房间,那么i + 1 房间不能偷,i + 2房间可偷可不偷,故如果知道i + 2时的最大值就可以计算。
  2. 不偷这个房间,那么i + 1 房间可偷可不偷,同1所讲,使用i + 1的最大值即可。
    对于这两个选择,我们要得到的是最大偷窃钱数,所以对两者选最大值即可。
    根据上述思路,得到状态转移方程:
dp[i] = Math.max(dp[i + 1], dp[i + 2] + nums[i];//nums为题目给定数组    dp为算法辅助数组

计算顺序

当面临选择计算顺序时,自顶向下即使用递归,自底向上即使用dp数组,通常情况下使用dp数组可以去优化空间复杂度,而且自顶向上为了提升效率须要使用额外的数组记录访问过的值,故通常选用自底向上更方便。代码如下:

class Solution {
    public int rob(int[] nums) {
        int[] dp = new int[nums.length + 2];//初始化最后两个位置不存在,令其为零,方便计算
        for(int i = nums.length - 1; i >= 0; i--){//自底向上
            dp[i] = Math.max(dp[i + 1], dp[i + 2] + nums[i]);//状态转移
        }
        return dp[0];//最后递推到dp[0]即为最大的偷窃金额
    }
}

优化空间

根据递推公式(状态转移方程)可以发现,每次计算dp[i]的值时,只与两个值dp[i + 1], dp[i + 2]有关,故只要使用两个变量存储这两个值即可,无须使用一个完整的数组,优化如下:

class Solution {
    public int rob(int[] nums) {
        int current = 0, previous = 0;//current计算之前即代表dp[i + 1],previous代表dp[i + 2]
        for(int i = nums.length - 1; i >= 0; i--){
            int temp = current;//备份dp[i + 1]   i--后current即为 dp[i + 2]
            current = Math.max(current, previous + nums[i]);
            previous = temp;
        }
        return current;
    }
}

对于current和previous初始化为0,可以理解为额外增加的两个房间nums[nums.length + 1]和nums[nums.length + 2] 里面没有钱,不影响最大值的计算,在DP问题中,初始化也是需要思考的一个环节。

打家劫舍Ⅱ

题目如下:


与第一题相比,主要的区别即是:房间形成了一个环,即第一个房间和最后一个房间不能同时偷窃。那么主要有三种情况:第一房间偷最后一个房间不偷、第一房间不偷最后一个房间偷、两个房间都不偷。可以以不偷为标准,将三种情况合为两种:不偷第一个房间、不偷最后一个房间。至于为什么选用不偷作为标准,可以想到,如果某一个房间不偷,完全可以删掉这个房间,那么这个问题就可以变成第一题的算法中取(0 - nums.length - 2, 1 - nums.length - 1)两个值的最大值即可,所以直接用第一题的算法两次即可。即

代码如下:
class Solution {
    public int rob(int[] nums) {
        if(nums.length == 1) return nums[0];
        
        int cur = 0;
        int pre = 0;

        int cur2 = 0;
        int pre2 = 0;
        //计算不偷最后一个房间的最大值
        for(int i = 0; i < nums.length - 1; i++){
            int temp = cur;
            cur = Math.max(cur, pre + nums[i]);
            pre = temp;
        }
        //计算不偷第一个房间的最大值
        for(int i = 1; i < nums.length; i++){
            int temp = cur2;
            cur2 = Math.max(cur2, pre2 + nums[i]);
            pre2 = temp;
        }

        return Math.max(cur, cur2);
    }
}

打家劫舍Ⅲ请见另一篇文章,使用树形DP。

相关文章

  • 198(213)-打家劫舍Ⅰ、Ⅱ - 比较典型的DP

    算是比较典型的DP(动态规划),虽然我还没开始刷DP的专题,不过跟着每日一题也做了几道,本着输出的原则把思路、算法...

  • LeetCode-198-打家劫舍

    LeetCode-198-打家劫舍 198. 打家劫舍[https://leetcode-cn.com/probl...

  • 补卡-打家劫舍

    198. 打家劫舍

  • LeetCode 第 198、213、337 题:“打家劫舍”

    LeetCode 第 198 题:打家劫舍 传送门:198. 打家劫舍。 你是一个专业的小偷,计划偷窃沿街的房屋。...

  • Leetcode 198 打家劫舍

    198. 打家劫舍[https://leetcode-cn.com/problems/house-robber/]...

  • 213、打家劫舍 II

    你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋...

  • LeetCode 198 打家劫舍

    198. 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是...

  • 198. 打家劫舍

    [toc] leetcode 难度:easy 题目 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现...

  • 198. 打家劫舍

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连...

  • 198. 打家劫舍

    https://leetcode-cn.com/problems/house-robber/

网友评论

    本文标题:198(213)-打家劫舍Ⅰ、Ⅱ - 比较典型的DP

    本文链接:https://www.haomeiwen.com/subject/vvmsyhtx.html