1. 程式人生 > >LeetCde#198: House Robber

LeetCde#198: House Robber

Description

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.
Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
             Total amount you can rob = 2 + 9 + 1 = 12.

Solution

解動態規劃題目一般有三種遞進的思路:

  1. 找到遞迴關係(或者說是狀態轉移方程)
  2. 使用遞迴
  3. 使用遞迴+備忘錄
  4. 使用迴圈+備忘錄
  5. 使用迴圈

再來看看這道題。如果搶劫當前房子的話,那麼上一個房子就不能被搶,此時的解為上上個房子的解加上當前房子的值;如果不搶劫當前房子的話,此時的解就為上個房子的解。因此,我們可以很容易的得到狀態轉移方程為f(n) = Max(f(n-2) + curr, f(n-1))

遞迴

public class Solution {
    public int rob(int[] nums) {
        return rob(nums, nums.length-1);
    }
    
    private int rob(int[] nums, int i) {
    	if(i < 0)
    		return 0;
    	return Math.max(rob(nums, i-2)+nums[i], rob(nums, i-1));
    }
}

遞迴+備忘錄

使用遞迴會產生很多重複的計算,因此可以使用一個備忘錄陣列來儲存已計算出的結果,避免重複遞迴。

public class Solution2 {
	int[] memo;	
	
    public int rob(int[] nums) {
    	memo = new int[nums.length];
    	Arrays.fill(memo, -1);
        return rob(nums, nums.length-1);
    }
    
    private int rob(int[] nums, int i) {
    	if(i < 0)
    		return 0;
    	if(memo[i] >= 0)
    		return memo[i];
    	int res = Math.max(rob(nums, i-2)+nums[i], rob(nums, i-1));
    	memo[i] = res;
    	return res;
    }
}

迴圈+備忘錄

遞迴操作可以使用迴圈替代。

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

迴圈

不使用備忘錄,而用兩個變數來儲存前兩個狀態的解。

public class Solution4 {
    public int rob(int[] nums) {
        if(nums.length == 0) return 0;
        if(nums.length == 1) return nums[0];
        int prev1 = nums[0];
        int prev2 = Math.max(nums[1], prev1);
        for(int i = 2; i < nums.length; i++) {
        	int curr = Math.max(prev1+nums[i], prev2);
        	prev1 = prev2;
        	prev2 = curr;
        }
        return prev2;
    }
}