樹形揹包O n v 2 入門

2022-05-09 13:12:08 字數 1813 閱讀 7434

我雖然做了好幾道樹形揹包的題,但是一直不是十分理解,對於每一道題,總是看題解就明白,然後換一道題自己寫不出來。臨近noip,gg讓我們強化一下揹包以及樹形揹包,我也恰有此打算,於是又開始從頭學習了樹形揹包。

看了好多部落格以及**之後,對樹形揹包確實有了乙個全新的認識,尤其是這篇部落格以及徐持恆的**《**積累揹包問題》,對我有很大的幫助。兩者都提到了泛化物品(當然這個名詞最初是在揹包九講裡面提到的)這個概念,我覺得這是對樹形揹包o(n * v2)做法的一種不同理解,不過我認為引入這個名詞的主要目的還是對o(nv)的做法做出了解釋。遺憾的是,我雖然用o(nv)的做法成功寫出了一道題,然而卻仍舊不是很懂。所以這篇博文主要是講解o(n * v2)的做法,也算是整理自己的學習筆記吧。

如果哪一天我把o(nv)的做法看懂了的話,可能還會來更這篇部落格。

上文已經提到,對於o(n * v2)的做法有兩種不同的理解,那麼我在這裡就分別闡述一下。

都以這道題為例

一、用分組揹包來理解

首先題中給的依賴關係是乙個森林,那麼可以建立乙個虛擬節點0,作為森林的根,形成一棵樹。

令dp[u][j]表示以 u 為根的子樹中,選 j 門課(體積)能得到的最大學分。那麼 u 一定要選(初始化dp[u][1] = val[u]),而對於子樹內其他點的選取情況,可以把每一種選取方案看成乙個物品,又因為每一種方案都是互斥的,每一組只能選乙個,那麼就是乙個分組揹包了。這裡的組數,是 u 的兒子個數 p = |son(u)|,對於乙個vi ∈son(u),他其實代表了j - 1個物品(因為還要選u),拿其中乙個為例,dp[vi][k](0 <= k < j)這種選取方案才代表乙個物品。

現在考慮轉移方程。按照分組揹包的寫法,我們應該先加一維,dp[u][k][j]表示以x為根的子樹,選到第k組,選了 j 門課得到的最大學分。於是有dp[u][k][j] = max(dp[u][k - 1][j], dp[u][k - 1][j - h] + dp[v][sz][h])。注意,dp[v][sz][h]代表乙個物品,sz是v的所有組數,因為要保證最優,所以一定從v的所有組數選完的狀態轉移到u。

然後再模仿分組揹包省去第二維,把 j 倒著列舉。

核心**:

1

void dfs(int

now)

210 }

對於每乙個節點只會進行一次o(v2)的分組揹包,所以複雜度o(n * v2)。

二、用泛化物品來理解

首先得解釋一下啥叫泛化物品:乙個價值隨體積改變而改變的物品,而且對於乙個體積 i,有對應的v[i]。

這個其實人人都見過,只不過沒有聽說這個名詞而已。比如求解01揹包就是泛化乙個物品的過程,得到的dp[i]就是乙個泛化物品。

還有這麼回事,泛化物品的和:有兩個泛化物品g1[i], g2[i],要將這兩個物品合併。做法就是對於每乙個體積 i ,列舉分配給這兩個物品的體積 j ,g[i] = max。複雜度o(v2)。

現在用泛化物品的概念看看樹形揹包。dp[u][j]表示的是u所在的泛化物品,則從子樹向上遞迴的時候,其實就是不斷地將u所在的泛化物品和他的子樹vi的泛化物品合併。合併一次的複雜度o(v2),一共n各節點,每合併一次減少乙個,所以總複雜度還是o(n * v2)。

**和上面完全相同,因為這本來就是對樹形揹包的兩種理解,而不是兩種寫法。

1

void dfs(int

now)

211 }

樹形揹包o(n * v2)的做法到此也基本講完了,但這其實都是基礎,深入的話還是得靠自己刷題去「悟」。還有一點就是如果哪位大佬會o(nv)的做法,能不能給我講講……

樹形依賴揹包

問題大意 給出一棵樹,根節點為1,每個點有毒素和收穫。要求毒素不超過給定值的情況下使收穫最大。乙個點的父親節點被選取後這個點才能被選取。首先弄出dfs序,也記錄下每個點其子樹及自身的大小。每個點都能夠被選或不選,如果選了才會考慮它子樹。設f i j 表示dfs序上第i位上的點在其子樹及自身上選取了毒...

樹形揹包總結

目錄 2 有物品大小 3 物品大小為1,有k的限制。二 dfs序上dp 例題1例題2 例題3總結下 樹形揹包,就是說,在樹上選乙個包含根的連通塊,或揹包存在依賴關係 選父才能選子 或者需要知道每個點的子樹中選了多少 通常,我們有兩種方法 我們設 dp i,j 表示在i的子節點中選j個的狀態。在轉移時...

樹形揹包DP

include using namespace std const int n 310,m n 2 int h n ne m v m idx int w n int dp n n int n,m void add int a,int b void dfs int u for int j m j 0 ...