【入門】Lintcode 392. 打劫房屋
阿新 • • 發佈:2018-12-14
假設你是一個專業的竊賊,準備沿著一條街打劫房屋。每個房子都存放著特定金額的錢。你面臨的唯一約束條件是:相鄰的房子裝著相互聯絡的防盜系統,且 當相鄰的兩個房子同一天被打劫時,該系統會自動報警。
給定一個非負整數列表,表示每個房子中存放的錢, 算一算,如果今晚去打劫,你最多可以得到多少錢 在不觸動報警裝置的情況下。
樣例
給定 [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]; } }