1. 程式人生 > >LeetCode-198. 打家劫舍

LeetCode-198. 打家劫舍

題目描述:你是一個專業的強盜,計劃搶劫沿街的房屋。每間房都藏有一定的現金,阻止你搶劫他們的唯一的制約因素就是相鄰的房屋有保安系統連線,如果兩間相鄰的房屋在同一晚上被闖入,它會自動聯絡警方。

給定一個代表每個房屋的金額的非負整數列表,確定你可以在沒有提醒警方的情況下搶劫的最高金額。

  1. 暴力搜尋方法
    思路:文中給出不能連續搶兩家,因此假設從最後一個房屋開始搶,最後一個房屋為index。將原問題分割成子問題,子問題的最優決策可以匯出原問題的最優決策。現有兩種可能情況,當前房屋搶和當前房屋不搶。若當前房屋搶,則下一房屋不能搶;若當前房屋不搶,則下一房屋可搶;選出這兩種情況的最大值,遞迴執行,直到index<0。
    public int solve(int index, int[] nums){
        if(index < 0){
            return 0;
        }
        int max = Math.max(nums[index] + solve(index - 2, nums), solve(index - 1, nums));
        return max;
    }

    public int rob(int[] nums) {
        return solve(nums.length-1, nums);
    }

這裡寫圖片描述
此種暴力方法在執行第56個測試用例時,超出時間限制。
假設我們搶n-1家,那麼接下來的執行方案:
n-1 ->(n-3, n-4, n-5)
假設我們搶n-2家,那麼接下來的方案為:
n-2 ->(n-4, n-5)
那麼我的兩種決策方式只是影響能不能搶n-3,在n-3之後都是隨便搶的;通過觀察上述兩種方案,我們發現了n-4,n-5被重複計算。因此,每一家都有兩種可能可能,搶或者不搶。則該演算法的時間複雜度為:O(2n)
為了避免上述的重複計算,我們初始化一個數組來記錄所有記錄為-1,如果當前index被算過,就記錄下來。因此,每次判斷該屋搶還是不搶後,都會得到一個index值。這就是去冗餘

,用採用空間換時間的方法。因此當n-1房屋的最優解算過後,就能推匯出n房屋的最優解。這就是動態規劃的思想。
因此,我們考慮使用動態規劃,設定result[]陣列記錄搶奪該房屋可能的最大收益。
自頂向下解法

class Solution {
    public static int[] result;

    public int solve(int index, int[] nums){
        if(index < 0){
            return 0;
        }

        if(result[index] >= 0){
            return result[index];
        }

        result[index] = Math.max(nums[index] + solve(index-2 , nums), solve(index-1, nums));
        return result[index];
    }

    public int rob(int[] nums) {
        result = new int[nums.length];
        for(int i=0; i < result.length; i++){
            result[i]=-1;
        }
        return solve(nums.length-1, nums);
    }
}

對於每個房屋我們都算了一次,那麼時間複雜度為O(n)
自底向上解法

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

        if (nums.length == 1){
            return nums[0];
        }

        if (nums.length == 2){
            return Math.max(nums[0], nums[1]);
        }

        int[] result = new int[nums.length];   
        result[0] = nums[0];
        result[1] = Math.max(nums[0], nums[1]);

        for(int index=2; index < result.length; index++){
            result[index] = Math.max(nums[index] + result[index-2], result[index -1]);
        }

        return result[nums.length -1];
    }
}

自底向上,編碼簡單,遞推(自頂向下為遞迴)。既然自底向上不需要遞迴,那麼就不需要solve函數了。我們只要處理好邊界條件,然後計算即可。