代碼隨想錄 47天 day198.打家劫舍 | | 337.打家劫舍 III | 213.打家劫舍II_Math

思路:當前房屋偷與不偷取決於前一個或前兩個房屋是否被偷。所以當前狀態和前面狀態會有一種依賴關係,這種依賴關係就是動規的遞推公式。

動規五部曲:

1.確定dp數組(dp table)及其下標的含義:dp[i]表示考慮下標i(包括i)以內的房屋,最多可以偷竊的金額為dp[i]。

2.確定遞推公式:決定dp[i]的因素就是第i個房間偷還是不偷。

(1)如果要偷第i個房間,那麼dp[i] = dp[i - 2] + nums[i]。即第i - 1個房間一定是不考慮的,找出下標為i - 2(包括i - 2)以內的房間,最多可以偷竊的金額為dp[i - 2]加上第i個房間偷到的錢。

(2)如果不偷第i個房間:那麼dp[i] = dp[i - 1],即考慮第i - 1個房間(注意這裏是考慮,並不是一定要偷第i - 1個房間)。

(3)然後dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i],dp[i - 1])。

3.dp數組如何初始化:

(1)從遞推公式dp[i] = max(dp[i - 2] + nums[i],dp[i - 1])可以看出,遞推公式的基礎就是dp[0]和dp[1]。

(2)從dp[i]的定義上來講,dp[0]一定是nums[0],dp[1]就是nums[0]和nums[1]的最大值,即dp[1] = max(nums[0],nums[1])。

4.確定遍歷順序:dp[i]是根據dp[i - 2]和dp[i - 1]推導出來的,所以一定是從前向後遍歷。

5.舉例推導dp數組:以[2,7,9,3,1]為例。

代碼隨想錄 47天 day198.打家劫舍 | | 337.打家劫舍 III | 213.打家劫舍II_滾動數組_02

附代碼:

(一)一維dp

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

(二)滾動數組,將空間複雜度降低為3個空間

//使用滾動數組思想優化空間
//分析本題可以發現,所求結果僅依賴於前兩種狀態,此時可用滾動數組思想將空間複雜度降低為三個空間
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if(len == 0){
            return 0;
        }
        else if(len == 1){
            return nums[0];
        }
        else if(len == 2){
            return Math.max(nums[0],nums[1]);
        }
        //存放選擇的結果
        int[] res = new int[3];
        res[0] = nums[0];
        res[1] = Math.max(nums[0],nums[1]);
        for(int i = 2;i < len;i++){
            res[2] = Math.max(res[0] + nums[i],res[1]);
            res[0] = res[1];
            res[1] = res[2];
        }
        return res[2];
    }
}

(三)進一步優化滾動數組,dp數組只存放與計算相關的兩次數據。

//進一步優化滾動數組空間,dp數組只存與計算相關的兩次數據
class Solution {
    public int rob(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        //初始化dp數組
        //優化空間,dp數組只用兩格空間,只記錄與當前計算相關的前兩個結果
        int[] dp = new int[2];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0],nums[1]);
        int res = 0;
        for(int i = 2;i < nums.length;i++){
            res = Math.max(dp[0] + nums[i],dp[1]);
            dp[0] = dp[1];
            dp[1] = res;
        }
        return dp[1];
    }
}