這篇是「兩個並列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,求當前值需要知道另乙個,求另乙個需要當前值,這樣...