1. 程式人生 > >dp基礎之序列型House Robber

dp基礎之序列型House Robber

問題:有一排房子N棟,房子i 裡有金幣 house[i],現有小偷想選擇而一些房子偷金幣,為了防止被抓,不能偷相鄰兩棟房子,問最多能偷多少錢?

分析;
在最優策略中,偷或不偷房子N-1
    1不偷,則最優策略就是前N-1棟房子的最優策略
    2偷,需要知道前N-1棟房子的最優策略且房子N-2不能偷。

子問題:
我們用f[i][0]表示在不偷房子i-1的情況下,前i-1棟房子的最優策略(即獲得最多金幣),f[i][1]表示偷房子i-1情況下前i-1棟房子的最優策略,則
    f[i][0] = max{f[i-1][0],f[i-1][1]}
    不偷房子i-1的最多金幣 = 所以房子i-2可以選擇偷或不偷,兩者取最大值
    f[i][1] = f[i-1][0] + house[i-1]
    偷房子i-1的得到最多金幣 = 不偷房子i-2的最多金幣+房子i-1的金幣
   
對於上面不偷房子i-1,其實即等於前i-2棟的最優策略即f[i-1]

故只需用f[i]表示竊賊在前i棟房子得到的最多金幣數
 
f[i]= max{f[i-1],f[i-2]+house[i-1]} 

竊賊在前i棟房子得到的最多金幣數 = max{不偷房子i-1得到最多的金幣(即在前i-1棟房子的最優策略);(偷房子i-1)竊賊在前i-1棟房子的得到的最多金幣+偷房子i-1} 

初始條件:
f[0] = 0(沒有房子,偷0金幣)
f[1] = house[0]

f[2] = max{f[1],f[0]+house[1]}
.
.
.
f[n]
時間複雜度O(N),空間複雜度為O(1)

 

程式碼及註釋如下:
 

def house_robber(house):
    #其實空間只要虛O(1),用兩個變數代替f[i-1]和f[i-2]即可
    n = len(house)
    if n==0:
        return 0
    f = [0 for i in range(n+1)]
    #初始化
    f[0],f[1] = 0,house[0]
    
    for i in range(2,n+1):
        f[i] = max(f[i-1],f[i-2]+house[i-1])
    return f[n]
house = [3,1,6]
print(house_robber(house))
結果為:9

 

若上問題改為房子不是一排,而是一圈,求最多獲取的金幣?

若房子不是一排,而是一圈,作何解?

分析:如果房子是一圈,則分情況討論
a:若房子0沒被偷,則房子N-1不需要考慮房子0,只需考慮房子N-2,跟前面的原問題情況一樣,只不過問題不是從0到N-1,而是直接從房子1到n-1
b:若房子N-1沒被偷,說明房子N-1也可以忘記,則直接從0到N-2

最後再取兩者的最大值即可