1. 程式人生 > >【入門】Lintcode 392. 打劫房屋

【入門】Lintcode 392. 打劫房屋

假設你是一個專業的竊賊,準備沿著一條街打劫房屋。每個房子都存放著特定金額的錢。你面臨的唯一約束條件是:相鄰的房子裝著相互聯絡的防盜系統,且 當相鄰的兩個房子同一天被打劫時,該系統會自動報警

給定一個非負整數列表,表示每個房子中存放的錢, 算一算,如果今晚去打劫,你最多可以得到多少錢 在不觸動報警裝置的情況下

樣例

給定 [3, 8, 4], 返回 8.

挑戰

O(n) 時間複雜度 且 O(1) 儲存。

解:

先以陣列arr[1,2,4,1,7,8,3]為例,要得到最優解,從最後陣列的數即arr[6]開始算,最佳方案記為OPT(6),則OPT(6)有兩種可能,要麼選了arr[6],要麼沒選

根據以上遞推式與遞推出口,可寫出以下程式碼:

public class Solution {
    /**
     * @param A: An array of non-negative integers
     * @return: The maximum amount of money you can rob tonight
     */

    public long houseRobber(int[] A) {
        // write your code here
        if(A==null || A.length<=0)
            return 0;
        
        return houseRobber(A, A.length-1);
    }
    
    //對於陣列arr,到達第i個數字的最佳方案是多少
    private long houseRobber(int[] arr, int i){
        //遞迴出口
        if(i == 0)
            return arr[0];
        if(i == 1)
            return Math.max(arr[0], arr[1]);
        
        //遞推式
        long A = houseRobber(arr, i-1);
        long B = houseRobber(arr, i-2) + arr[i];
        
        return Math.max(A, B);
    }
}

但是,這樣會解很多重疊子問題,比如上圖中,重複計算很多次OPT(3), OPT(4),時間複雜度為O(2^N)

所以必須想辦法將子問題的解用陣列存起來,自底向上迴圈求解。

public class Solution {
    /**
     * @param A: An array of non-negative integers
     * @return: The maximum amount of money you can rob tonight
     */

    public long houseRobber(int[] A) {
        // write your code here
        if(A==null || A.length<=0)
            return 0;
        
        return dp_opt(A);
    }
    
    private long dp_opt(int[] arr){
        long[] opt = new long[arr.length]; //存放子問題的最優解
        
        //初始值,對應於上面的遞推出口
        opt[0] = arr[0];
        if(opt.length == 1)
            return opt[0];
        opt[1] = Math.max(arr[0], arr[1]);
        
        //迴圈獲得子問題的最優解,對應於上面的遞推式
        for(int i=2 ; i<arr.length ; i++){
            long A = opt[i-2] + arr[i];
            long B = opt[i-1];
            
            opt[i] = Math.max(A, B);
        }
        
        return opt[opt.length-1];
    }
    
}