DP 兩個並列DP HouseRobber系列

2021-10-06 02:36:24 字數 4801 閱讀 6209

這篇是「兩個並列dp」的第一篇,先寫典型的houserobber系列。相鄰的房子不能都偷(即不能2-in-a-row)。

後面再加乙個paint fence,是不能有超過兩個連續的(即不能3-in-a-row)。

特點是:需要分情況討論當前的選擇、

做法是:維護兩個dp,維護的時候兩者互相影響。

偷當前元素,robcur

不偷當前元素,notrobcur 題目

簡介198. house robber

213. house robber ii

337. house robber iii

276. paint fence

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.

分情況討論:

我們決定偷當前元素:則要確定robcur[i]。

不能偷前乙個,即可以繼承notrobcur[i-1]的累積收入

當前元素可以加入累積收入,nums[i]

我們決定不偷當前元素,則要確定notrobcur[i]。

不偷當前的,於是前乙個可以偷也可以不偷,但注意!!並不能將robcur[i-1]和notrobcur[i-1]加起來,因為這兩者是互斥的,兩個狀態不能同時都要。

比如,robcur[i-1]定義為「如果我們決定偷i-1元素的前提下,能累積收入的最大值「,和notrobcur[i-1]是條件上互斥的)

於是,是取兩種選擇的max

class

solution

int len = nums.length;

int[

] robcur =

newint

[len]

;int

notrobcur =

newint

[len]

; robcur[0]

= nums[0]

; notrobcur[0]

=0;for

(int i =

1; i < len; i++

)return math.

max(robcur[len-1]

, notrobcur[len-1]

);}}

input: [1,2,3,1] 和198.house robber i區別在於:那個是一行,這個是排成環。output: 4

explanation: rob house 1 (money = 1) and then rob house 3 (money = 3).

total amount you can rob = 1 + 3 = 4.

怎麼把一行拓展到環呢?環和行區別在於:環相當於兩端又增加了額外的「鄰居」,需要再分兩種情況討論:

偷第乙個(不是非得偷第乙個,而是承諾一定不偷最後乙個)。於是我們就假裝只存在第2到最後乙個房子,[2,3,1]

不偷第乙個。於是我們就假裝只存在第1到倒數第二個房子,[1,2,3]

然後就劃歸到「一**子」的問題。

所以本題就掃兩遍,取較大的結果。

另外此處我們把dp需要額外的o(n)空間減小到o(1),比起198的碼稍微優化了一點。

class

solution

else

if(len ==1)

//情況1:偷第乙個(承諾不偷最後乙個)

int notrobcur =

0, robcur =0;

for(

int i =

0; i < len -

1; i++

)int max1 = math.

max(robcur, notrobcur)

;//情況2:承諾不偷第乙個

就和上面的一行和環的,本質是一樣的,只是這裡的鄰居不是用「下標相鄰」表示的,而是用tree的父子關係表示。

如果偷當前節點,則它的左孩子和右孩子都是鄰居,都不能偷。

如果不偷當前節點,注意!不是「必須偷左右孩子」,而是「可以偷左右孩子也可以不偷」 (在第三個notrobcur() 中,不是呼叫第二個robcur(),而是呼叫第乙個rob())

class

solution

return math.

max(

robcur

(root)

,notrobcur

(root));

}private

introbcur

(treenode node)

return

notrobcur

(node.left)

+notrobcur

(node.right)

+ node.val;

}private

intnotrobcur

(treenode node)

return

rob(node.left)

+rob

(node.right);}

}

上面是不能2-in-a-row的,下面是不能3-in-a-row的:

input: n = 3, k = 2 n個樁子,k種顏色可以使用,不能有超過兩個樁子同色。

output: 6

explanation: take c1 as color 1, c2 as color 2. all possible ways are:

post1 post2 post3

1 c1 c1 c2

2 c1 c2 c1

3 c1 c2 c2

4 c2 c1 c1

5 c2 c1 c2

6 c2 c2 c1

這個題比house robber相比,相當於公升維了,有點難想清楚。

因為規則是不能3個連到一起,於是我們要把dp[i] 和dp[i-1] 兩者合併起來考慮,作為整體的狀態在更新:

分兩種情況:

當前兩個樁子同色:curtwosame

當前兩個樁子不同色:curtwodiff

在更新的時候,

當前樁子dp[i] 顏色的決策,受到前面兩個樁子的影響。

(house robber只受前乙個鄰居影響,只考慮dp[i-1]即可)

確定curtwosame的時候,分兩種情況:

dp[i-2]和dp[i-1]顏色相同,比如黃色

=> 則當前不能是黃色(否者違規),然而又要求curtwosame,就矛盾了。

這種情況計數為0。

應用乘法原理,0 * curtwosame[i-1]

dp[i-2]和dp[i-1]顏色不同,比如藍+黃

=> 則當前可在k種顏色中選一種。又要求curtwosame,只能是黃色。

這種情況計數為1。

應用乘法原理,1 * curtwodiff[i-1]

確定curtwodiff的時候,分兩種情況:

dp[i-2]和dp[i-1]顏色相同,比如黃色

=> 則當前不能是黃色,可以從k-1種顏色裡挑。而又要求curtwodiff,恰好滿足不是黃色。

這種情況計數為k-1。

應用乘法原理,(k-1) * curtwosame[i-1]

dp[i-2]和dp[i-1]顏色不同,比如藍+黃

=> 本來可以從k種顏色裡挑,但又要求curtwodiff,所以只剩k-1種。

這種情況計數為k-1。

應用乘法原理,(k-1) * curtwodiff[i-1]

最後的結果,注意!和house robber不同,這裡的curtwodiff和curtwosame是兩種情況,都有可能,於是應該用「加法原理」求和;而robcur和notrobcur是互斥事件,是不能同時存在的。

(這個地方稍微有點沒想清楚為啥不一樣呵呵,等我變聰明了來補充)

class

solution

else

if(n ==1)

int curtwodiff = k *

(k -1)

;int curtwosame = k;

for(

int i =

2; i < n; i++

)return curtwodiff + curtwosame;

}}

兩個約束下的dp問題

洛谷p1510 分析 本質上還是乙個01揹包,將體力當做重量,體積當做價值。配上滾動陣列 即dp j 代表在體力耗費為j時最大能搬運多少體積的石頭,當dp j v時就說明存在滿足情況的解,這樣,就選擇最小的j就可以了 includeusing namespace std typedef long l...

UVA 10859 同時dp兩個變數 樹形dp

同時dp兩個變數 即 在 v1 最小的情況下 v2 最小 可以令dp的內容為 v1 m v2 使得這個數值最小,m是乙個比 v2最大值還要大的值。這個題中,v1 為街燈的數量,v2 為被兩個街燈照亮的數量 要求是 v1 最小 v2 最大 令 v2為 乙個街燈照亮的路的數量,這樣方向就一致了。一共有兩...

兩個矩陣中的dp題的差異

給定乙個由 0 和 1 組成的矩陣,找出每個元素到最近的 0 的距離。分析 很容易想到dp i j min dp i 1 j dp i j 1 dp i 1 j dp i j 1 出口是若m i j 0,則dp i j 0。但是有個問題,兩個相鄰的1,求當前值需要知道另乙個,求另乙個需要當前值,這樣...