Leetcode 213 House Robber II 搶劫最大金額

原題鏈接

https://leetcode.com/problems/house-robber-ii/

題目描述

Note: This is an extension of House Robber.
注意:這題是House Robber的擴展,解題詳見Leetcode 198 House Robber 搶劫最大金額

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.
搶劫完那條街道上的房子之後,這個賊有找到了實施自己盜竊行動的另一個地點,這樣他纔不會引來太多注意。這次,這裏的搜有房子排成了一個圓圈,這意味着第一個房子和最後一個房子也是挨着的。與此同時,房子的安保系統還是和之前那條街上的一樣(不能同時搶劫兩所相鄰的房子,否則會報警)。

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.
給出一個非負整數數組,代表每個房子內的金錢數額,計算你今晚在不驚動警方的前提下可以搶劫的最大金額。

Tag Dynamic Programming

解題思路

作爲House Robber(查看詳解)的擴展題,這道題跟其非常相似,唯一不同的地方是現在多了一個條件,房子圍成圈,即第一個房子和最後一個房子是相鄰的。我們之前對House Robber的解決方案在這裏肯定行不通,因爲我們沒有考慮第一個房子和最後一個房子不能同時搶劫的問題。然而,之前的解決方案也不是完全沒有用處,我們可以把問題化簡爲House Robber問題,那麼該如何做呢?

這裏我們可以這麼來考慮。對於房子中的任意一所房子A,我們在搶劫最大金額的情況下,只有兩種情況:(1)搶劫A,(2)不搶A

圍成圈的房屋
  A B C D E F G H I
 T                 J
  S R Q P O N M L k

(1) 當我們搶劫A的時候
與A相鄰的房子不能搶了,這時剩下的房子可以隨意搶劫,除去A及其相鄰的房子就可以看成是不成環的一條街上的房子,這時就變成House Robber問題了,最大金額sum1=nums[A]+robStreet(除去A及其相鄰房子的所有房子)。robStreet表示按照House Robber問題中搶劫一條街上的房子來計算得到最大金額

除去A及其相鄰的房屋,計算robStreet(除去A及其相鄰的房子)
      C D E F G H I
                   J
  S R Q P O N M L k  

等同於一條街上的房屋
C D E F G H I J K L M N O P Q R S

(2) 當我們不搶A的時候
除去A的所有房子都可以隨意搶劫,除去A之外的所有房子就可以看成是不成環的一條街上的房子,這時也是House Robber問題,最大金額sum2=robStreet(除去A的所有房子)。

除去A的所有房屋,計算robStreet(除去A的所有房子)
    B C D E F G H I
 T                 J
  S R Q P O N M L k  

等同於一條街上的房屋
B C D E F G H I J K L M N O P Q R S T

有了sum1和sum2之後,其中的較大者就是我們要求的值。即max=bigger(sum1, sum2)

特別的,在書寫代碼時,我們可以選取第0個房子作爲A,實際上選取哪一個對結果並沒有影響。

代碼

/**
 * 搶劫環狀社區獲得最大金額
 * 不能搶劫相鄰房屋,房屋呈環狀,首尾相接
 * @param nums 每個房子內的金額
 * @param numsSize 房屋數量
 * @return 最大金額
 */
int rob(int* nums, int numsSize) {
    int rotStreet(int*, int);
    int bigger(int, int);

    /* 對於這個問題,我們選取任一房子A
     * 則在搶劫最大金額時,對A僅有兩種情況:
     * 要麼搶劫A,要麼不搶劫A
     * (1)如果搶劫A,則A左右相鄰的房子B和C一定不能搶,
     * 這時除去ABC的其他房子就可以看成是首尾不相接的一條街上的房子
     * 這時最大金額爲sum1=nums[A]+robStreet(除去ABC的其他所有房子);
     * (2)如果不搶A,則除去A之外的所有房子可以看成是一條接上的房子
     * 這時最大金額爲sum2=robStreet(除去A的其他所有房子)
     * sum1和sum2中較大者就是待求解
     * 特別的,我們可以選擇第0所房子作爲A */

    if (numsSize <= 0) return 0;
    if (numsSize == 2) return bigger(*nums, *(nums + 1));

    // 搶劫A時的最大金額
    int sum1 = *nums + robStreet(nums + 2, numsSize - 3);
    // 不搶A時的最大金額
    int sum2 = robStreet(nums + 1, numsSize - 1);

    return bigger(sum1, sum2);
}

/**
 * 搶劫一條街上的房子,計算可能搶劫的最大數額
 * 不能搶劫相鄰的房子
 * @param nums 每個房子中的金額
 * @param numsSize 房子數量
 * @return 搶劫的最大數額
 */
int robStreet(int* nums, int numsSize) {
    int bigger(int, int);

    if (numsSize <= 0) return 0;

    // 搶劫某間房子時,可以從後面的所有房子裏搶劫的最大金額
    int *maxWhenRob = (int *)malloc(sizeof(int) * numsSize);
    // 不搶劫某間房子時,可以從後面的所有房子裏搶劫的最大金額
    int *maxWhenNotRob = (int *)malloc(sizeof(int) * numsSize);

    int index = numsSize - 1;
    /* 初始化最後一天的數據 */
    *(maxWhenRob + index) = *(nums + index);
    *(maxWhenNotRob + index) = 0;
    /* 循環處理之前的房子 */
    for (--index; index > -1; --index) {
        // 搶劫某間房子一定不能搶後面的相鄰的房子
        *(maxWhenRob + index) = *(nums + index) + *(maxWhenNotRob + index + 1);
        // 不搶某間房子,其後面的房子可以搶也可以不搶,選其中的較大者
        *(maxWhenNotRob + index) = bigger(
                *(maxWhenRob + index + 1), *(maxWhenNotRob + index + 1));
    }

    int max = bigger(*maxWhenRob, *maxWhenNotRob);
    free(maxWhenRob);
    free(maxWhenNotRob);
    return max;
}

/** 返回兩者中的較大者 */
int bigger(int a, int b) { return a > b ? a : b; }

完整代碼 https://github.com/Orange1991/leetcode/blob/master/213/c/main.c

測試數據

[4,3,5,6,1,2,0,4,2,5] : 20

同一組數據在House Robber題目中計算結果爲21.


2015/8/19

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章