最近在練樹形 DP,正好看到 這一道 虛標的紫題,但本蒟蒻不會寫,想出來了便記錄一下
先看題面,一顆有根樹,選定 k 個節點作為 ”伐木場“,求運送木料最小費用。注意木料費用是 dis * wood。
最開始想到簡單的樹形揹包,狀態轉移方程:
1 | f[i][k] = min(f[j][s] + cost, f[i][k]); |
但是注意到,如果某個後代節點如果是伐木場,cost 不需要計算,所以狀態中還需要儲存後代的伐木場情況。
但是後代中鋸木廠不止一個。考慮到樹有唯一的父親,且同一深度祖先唯一,可以記錄最近的伐木場祖先,作為狀態的一部分。
有 f[i][j][k] 即 i 節點 最近的伐木場祖先為 j,後代(不算自己)有 k 個是伐木場。
但是由於 f 自己也可能是伐木場,轉移方程不同,需要分類討論,於是改成:f[i][j][k][0/1] 其中 0 代表自己不是伐木場, 1 表示自己是伐木場。
因為我們需要列舉祖先,在 DFS 時需要記錄 Fa 陣列:
1 | DFS 開始時:fa[++tot] = x; |
簡單記錄祖先 stack。
回溯後,對於當前節點 x 和 子節點 y 每個祖先 ff:
1 | 先對每個 k 賦初值,對於每種伐木場個數 l: |
然後就是樹形揹包:
1 | f[x][ff][l][0] = min(f[x][ff][l][0], f[x][ff][l-s][0] + f[y][ff][s][0]); --> 當前節點不是伐木場,對 y 節點分配 s 個伐木場個數 |
注意在列舉 l 時,由於是01揹包,需要倒過來列舉,不然狀態計算會疊加。(很容易理解)
做完了?好像還差一個 cost!
考慮到 祖先節點 ff cost 的貢獻,因為是 當前節點 W 乘以 到 ff 的總距離!
需要計算總距離,維護 dep 陣列,即當前節點到根節點距離,即可!很容易理解。ff 到 x 的距離就是 dep[x] - dep[ff]
對於當前節點 x 每一個祖先 ff:
1 | if(l>=1){ --> 因為 l 需要 -1 ,要分類討論。 |
這裡再來聊一聊合併。為了便於討論,上文揹包轉移的時候,我們沒有考慮 y 的 1 的情況,而是在每次 y 回溯時 合併 0 和 1。這有點類似於滾動陣列?回溯後 0 不再表示之前的意義,而是我們最初設計的狀態:i 節點 最近的伐木場祖先為 j,後代(或自己)有 k 個是伐木場!
1 | #include <cmath> |
最近在練樹形 DP,正好看到 這一道 虛標的紫題,但本蒟蒻不會寫,想出來了便記錄一下
先看題面,一顆有根樹,選定 k 個節點作為 ”伐木場“,求運送木料最小費用。注意木料費用是 dis * wood。
最開始想到簡單的樹形揹包,狀態轉移方程:
1 | f[i][k] = min(f[j][s] + cost, f[i][k]); |
但是注意到,如果某個後代節點如果是伐木場,cost 不需要計算,所以狀態中還需要儲存後代的伐木場情況。
但是後代中鋸木廠不止一個。考慮到樹有唯一的父親,且同一深度祖先唯一,可以記錄最近的伐木場祖先,作為狀態的一部分。
有 f[i][j][k] 即 i 節點 最近的伐木場祖先為 j,後代(不算自己)有 k 個是伐木場。
但是由於 f 自己也可能是伐木場,轉移方程不同,需要分類討論,於是改成:f[i][j][k][0/1] 其中 0 代表自己不是伐木場, 1 表示自己是伐木場。
因為我們需要列舉祖先,在 DFS 時需要記錄 Fa 陣列:
1 | DFS 開始時:fa[++tot] = x; |
簡單記錄祖先 stack。
回溯後,對於當前節點 x 和 子節點 y 每個祖先 ff:
1 | 先對每個 k 賦初值,對於每種伐木場個數 l: |
然後就是樹形揹包:
1 | f[x][ff][l][0] = min(f[x][ff][l][0], f[x][ff][l-s][0] + f[y][ff][s][0]); --> 當前節點不是伐木場,對 y 節點分配 s 個伐木場個數 |
注意在列舉 l 時,由於是01揹包,需要倒過來列舉,不然狀態計算會疊加。(很容易理解)
做完了?好像還差一個 cost!
考慮到 祖先節點 ff cost 的貢獻,因為是 當前節點 W 乘以 到 ff 的總距離!
需要計算總距離,維護 dep 陣列,即當前節點到根節點距離,即可!很容易理解。ff 到 x 的距離就是 dep[x] - dep[ff]
對於當前節點 x 每一個祖先 ff:
1 | if(l>=1){ --> 因為 l 需要 -1 ,要分類討論。 |
這裡再來聊一聊合併。為了便於討論,上文揹包轉移的時候,我們沒有考慮 y 的 1 的情況,而是在每次 y 回溯時 合併 0 和 1。這有點類似於滾動陣列?回溯後 0 不再表示之前的意義,而是我們最初設計的狀態:i 節點 最近的伐木場祖先為 j,後代(或自己)有 k 個是伐木場!
1 | #include <cmath> |