樹形dp,即在樹上進行的動態規劃,由於樹固有的遞迴性質,因此樹形dp往往也遞迴進行。
某大學有 n這道題經常被用來入門樹形dp,因為其非常明確的狀態定義。nn 個職員,編號為 1....
n1....n
1....n
。他們之間有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。
現在有個周年慶宴會,宴會每邀請來乙個職員都會增加一定的快樂指數 r
ir_i
ri,但是呢,如果某個職員的直接上司來參加舞會了,那麼這個職員就無論如何也不肯來參加舞會了。
所以,請你程式設計計算,邀請哪些職員可以使快樂指數最大,求最大的快樂指數。
我們令 dp[
i]
dp[i]
dp[i
] 為以 i
ii 結點為根的子樹的最優解,同時我們還需要討論,結點 i
ii 是否選取,所以可以在原陣列上加入一維,變成 dp[
i][2
]dp[i][2]
dp[i][
2] dp[
i][0
]dp[i][0]
dp[i][
0],表示 i
ii 不參加舞會的情況,i
ii 子樹的最優解。
d p[
i][1
]dp[i][1]
dp[i][
1],表示 i
ii 參加舞會的情況,i
ii 子樹的最優解。
容易想到:當 i
ii 不參加舞會時,i
ii 的子節點,可以參加舞會,也可以不參加舞會,所以這部分轉移方程為:
d p[
u][0
]=∑m
ax(d
p[v]
[0],
dp[v
][1]
)dp[u][0] = \sum max(dp[v][0], \ \ dp[v][1])
dp[u][
0]=∑
max(
dp[v
][0]
,dp[
v][1
])當 i
ii 參加舞會時,i
ii 的子節點不能參加舞會,因此只能從 dp[
v][0
]dp[v][0]
dp[v][
0]轉移過來。
d p[
u][0
]=∑d
p[v]
[0
]dp[u][0] = \sum dp[v][0]
dp[u][
0]=∑
dp[v
][0]
由於乙個結點的轉移方程需要其子節點的資料,所以轉移是在遞迴返回時進行的,搜尋部分**:
void dfs(int u, int fa)
}
樹形dp和線性dp一樣,需要考慮狀態的定義和轉移方程,只是由於樹結構上的特性,需要確定轉移順序,有些複雜的dp問題還需要多次dfs搜尋。
樹上揹包也叫有依賴的揹包問題,是揹包問題和樹形dp的結合。問題的一般形式:
有 n為了方便起見,我們可以對每個沒有依賴的物品新增乙個依賴 「0號物品」,即給森林的每棵樹的根節點建立乙個超級源點,這個點的價值和費用都為0,這樣避免了處理森林的麻煩。nn 個物品,每個物品有其 價值 val
[i
]val[i]
val[i]
,重量 wei
[i
]wei[i]
wei[i]
,同時物品之間可能存在依賴關係,選擇物品 i
ii 則必須選擇物品 fa[
i]
fa[i]
fa[i
] ,依賴關係形成森林,求總重量不超過 w
ww 所能獲取的最大價值。
考慮子問題,設 dp[
u][j
]dp[u][j]
dp[u][
j]為 以 u
uu 結點為根的子樹,費用不超過 j
jj 的最大價值。
假設 u
uu 有若干子節點 v1,
v2..
....
vn
v_1, \ v_2......v_n
v1,v2
...
...v
n,如果把每個子樹看作一組物品,可以把這些子樹看作組內決策互斥的 n
nn 組物品,所以相當於每個結點都對其所有子節點做了一次分組揹包。
//v是費用 w是價值
void dfs(int u, int fa)
}}
樹形 dp 中的換根 dp 問題又被稱為二次掃瞄,通常不會指定根結點,並且根結點的變化會對一些值,例如子結點深度和、點權和等產生影響。
通常需要兩次 dfs,第一次 dfs 預處理諸如深度,點權和之類的資訊,在第二次 dfs 開始執行換根動態規劃。
問題:
給定乙個 個點的樹,請求出乙個結點,使得以這個結點為根時,所有結點的深度之和最大。不妨令 u
uu 為當前結點,v
vv 為當前結點的子結點。首先需要用 s
is_i
si 來表示以 i
ii 為根的子樹中的結點個數,並且有 su=
∑s
vs_u = \sum s_v
su=∑s
v。顯然需要一次 dfs 來計算所有的 s
is_i
si,這次的 dfs 就是預處理,我們得到了以某個結點為根時其子樹中的結點總數。
考慮狀態轉移,這裡就是體現 「換根」 的地方了。令 dp[
u]
dp[u]
dp[u
] 為以 u
uu 為根時,所有節點的深度之和。
我們需要以 u
uu 為根轉移到以 v
vv 為根。顯然在換根的過程中,以 v
vv 為根或以 u
uu 為根會導致其子樹中的結點的深度產生改變。具體表現為:
於是在第二次 dfs 遍歷整棵樹並狀態轉移 dp[
v]=d
p[u]
+n−2
∗s
vdp[v]=dp[u]+n-2*s_v
dp[v]=
dp[u
]+n−
2∗sv
,那麼就能求出以每個結點為根時的深度和了。最後只需要遍歷一次所有結點深度和就可以求出答案。
void dfs1(int u, int fa) //求出size陣列
}void dfs2(int u, int fa)
}
( 動態規劃專題 ) 樹形dp
動態規劃專題 樹形dp 直接看例題 p2015 二叉蘋果樹 有一棵蘋果樹,如果樹枝有分叉,一定是分2叉 就是說沒有只有1個兒子的結點 這棵樹共有n個結點 葉子點或者樹枝分叉點 編號為1 n,樹根編號一定是1。我們用一根樹枝兩端連線的結點的編號來描述一根樹枝的位置。下面是一顆有4個樹枝的樹 2 5 3...
動態規劃(7) 樹形DP
昨天本來就能寫的,結果寫了倆個題寫了四五個小時,於是乎拖到今天了。這個題就很好寫了,不像狀態壓縮,真的是難理解 大佬部落格 f u 0 表示從以u為根節點,不選u的方案。f u 1 表示從以u為根節點,選u的方案。所以f u 0 max f si 0 f si 1 i是遍歷下一層的子節點 f u 0...
動態規劃二(樹形DP)
顧名思義,是指將dp建立在樹狀結構的基礎上。問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接...