1. 程式人生 > >leetcode 213. 打家劫舍II

leetcode 213. 打家劫舍II

題目描述:

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

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

思路:與上一題“打家劫舍”相比,唯一的變化在於房屋圍成了一圈。類似上一篇,假定總共有n間房子(0~n-1號),考察0號房間和1號房間,如果第一步選擇0號房間,那麼一定不會選與之相鄰的n-1號房間。這樣問題演化為偷取0~n-2號房間。如果第一步選擇1號房間,問題同樣轉化為偷取1~n-1號房間。這樣就把一個問題劃分成了兩個子問題,我們可以分別求出它們的最優解,然後再取更好的那個。

但是還有個問題:偷取0~n-2號房間時的最優解,不一定會偷取0號房間啊?這與我們的假設(第一步一定偷取0號房)不相符,會不會導致錯過最優解?這個擔心是多餘的。如果最優解中沒有偷取0號房間,那麼最優解必然是1~n-1號房間問題的最優解;否則它就是0~n-2號房間問題的最優解。所以無論如何,最優解都會被找到。

這兩個子問題性質與上一個問題“打家劫舍”相同,所以也採用了相同的解法。

int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==0) return 0;
        if(n==1) return nums[0];
        //注:m[i]代表從房間i出來時,身上的金錢總量
        vector<int> m(n);
        //情況1,偷0~n-2號房間
        m[0]=nums[0],m[1]=max(nums[0],nums[1]);
        for(int i=2;i<n-1;i++)
            m[i]=max(m[i-1],m[i-2]+nums[i]);
        int ans1=m[n-2];
        
        //情況2,偷1~n-1號房間
        m[1]=nums[1],m[2]=max(nums[1],nums[2]);
        for(int i=3;i<n;i++)
            m[i]=max(m[i-1],m[i-2]+nums[i]);
        int ans2=m[n-1];
        //返回總問題的最優解
        return max(ans1,ans2);
    }

測試執行時間0ms,難以置信。演算法時間複雜度和空間複雜度都是O(n)。空間可以優化到O(1),但是那樣會使時間複雜度的常數項係數增大,損失一些時間上的效能。