1. 程式人生 > >代碼題(42)— 打家劫舍

代碼題(42)— 打家劫舍

log 報警 防盜系統 pro == rand 公式 robber 你在

1、198. 打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [1,2,3,1]
輸出: 4
解釋: 偷竊 1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3)。
     偷竊到的最高金額 = 1 + 3 = 4 。

示例 2:

輸入: [2,7,9,3,1]
輸出: 12
解釋: 偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接著偷竊 5 號房屋 (金額 = 1)。
     偷竊到的最高金額 = 2 + 9 + 1 = 12 。

  這道題的本質相當於在一列數組中取出一個或多個不相鄰數,使其和最大。那麽我們對於這類求極值的問題首先考慮動態規劃Dynamic Programming來解,我們維護一個一位數組dp,其中dp[i]表示到i位置時不相鄰數能形成的最大和,那麽遞推公式怎麽寫呢,我們先拿一個簡單的例子來分析一下,比如說nums為{3, 2, 1, 5},那麽我們來看我們的dp數組應該是什麽樣的,首先dp[0]=3沒啥疑問,再看dp[1]是多少呢,由於3比2大,所以我們搶第一個房子的3,當前房子的2不搶,所以dp[1]=3,那麽再來看dp[2],由於不能搶相鄰的,所以我們可以用再前面的一個的dp值加上當前的房間值,和當前房間的前面一個dp值比較,取較大值當做當前dp值,所以我們可以得到遞推公式dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 由此看出我們需要初始化dp[0]和dp[1],其中dp[0]即為num[0],dp[1]此時應該為max(num[0], num[1]),

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty())
            return 0;
        vector<int> dp(nums.size());
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        
        for(int i=2;i<nums.size();++i)
        {
            dp[i] = max(dp[i-2
]+nums[i], dp[i-1]);//動態規劃,到第i家為止最大和 } //return dp[nums.size()-1]; return dp.back(); } };

2、213. 打家劫舍 II

你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金。這個地方所有的房屋都圍成一圈,這意味著第一個房屋和最後一個房屋是緊挨著的。同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [2,3,2]
輸出: 3
解釋: 你不能先偷竊 1 號房屋(金額 = 2),然後偷竊 3 號房屋(金額 = 2), 因為他們是相鄰的。

示例 2:

輸入: [1,2,3,1]
輸出: 4
解釋: 你可以先偷竊 1 號房屋(金額 = 1),然後偷竊 3 號房屋(金額 = 3)。
     偷竊到的最高金額 = 1 + 3 = 4 。

  現在房子排成了一個圓圈,則如果搶了第一家,就不能搶最後一家,因為首尾相連了,所以第一家和最後一家只能搶其中的一家,或者都不搶,那我們這裏變通一下,如果我們把第一家和最後一家分別去掉,各算一遍能搶的最大值,然後比較兩個值取其中較大的一個即為所求。那我們只需參考之前的House Robber 打家劫舍中的解題方法,然後調用兩邊取較大值。

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n<=0)
            return 0;
        if(n == 1)
            return nums[0];
        return max(rob(nums,0,n-1), rob(nums,1,n));// 去掉第一個或者最後一個,分別求值找最大值
        
    }
    int rob(vector<int> &nums, int left, int right)
    {
        if(right-left<=1)
            return nums[left];
        vector<int> dp(right);
        dp[left] = nums[left];
        dp[left+1] = max(nums[left], nums[left+1]);
        for(int i = left+2;i<right;++i)
        {
            dp[i] =max(dp[i-1], dp[i-2]+nums[i]);
        }
        return dp.back();
    }
};

代碼題(42)— 打家劫舍