先序遍歷用於優化樹形分組揹包問題

2021-08-08 23:45:08 字數 2200 閱讀 8319

** 國家集訓隊 何森 **

【問題描述】

什麼是樹形揹包?簡單的說就是在一棵有n個節點的樹上,每個節點都相當於一件物品,每件物品都有乙個**和乙個得分對於物品a,設其父節點為father[a],那麼你必須購買了father[a]後才能購買a。現給你m元現金,要求求乙個購買方案,使得物品的總得分最大。

我們見過很多此類題目:重修道路、技能樹、選課……

【分析】

我們很容易想到乙個樹形動態規劃:

cost[a]表示a節點所代表的物品的**

score[a]表示a節點所代表的物品的得分

狀態f[a][b]表示以節點a為根的子樹,總共花費不超過b元的最多得分。

狀態轉移方程:

f[a][b]=max+score[a];

其中ci為a的子節點;∑bi=b-cost[a];

這樣列舉的效率顯然不高。

於是我們想到樹形dp的常用優化方法:利用左兒子右兄弟表示法

這樣f[a][b]表示以節點a為根的子樹且及其兄**樹總共花費不超過b元的最多得分。

f[a][b]=max(max,f[right[a]][b]);

其中left[a],right[a]分別表示a的左、右子樹。

這樣我們得到了乙個o(m2n)的動態規劃。

然而,我們探索的腳步還未停下。

回想經典的01揹包問題。

f[a][b]表示從第a個物品到第n個物品總共花費不超過b元所能得到的最大得分。顯然我們要做的就是列舉乙個物品買還是不買。

f[a][b]=max(f[a+1][b-cost[a]]+money[a],f[a+1][b]);

複雜度是o(m*n)的。

為什麼這個問題可以做到o(m*n)呢,樹形揹包可以優化到這個複雜度嗎?

我們來**樹形揹包的複雜度是怎麼來的。

我們從最原始的多叉樹說起:

在狀態轉移的時候,我們需要列舉給每棵子樹分配的現錢數,這導致了列舉量巨大,演算法效率不高。

根據這一點我們把多叉樹轉化成了二叉樹,目的就是要減少列舉量,顯然我們做到了,在二叉樹上我們只需要列舉給左子樹分配多少。

關於樹形揹包先研究到這裡,我們先看看經典的揹包是怎麼回事:

當我們在考慮乙個物品a的時候,只用列舉是否要購買它,而且注意到不論我們是否要購買,剩下的錢都由後面所有的物品來分。

回到樹上來。

我們發現,在乙個分叉點上我們的現錢是需要分流的,即給每一棵子樹一部分。

而且在子樹中不會相互轉移了。

本質的差異就在這裡。

這時,我們就有了乙個美妙的設想:將左子樹和右子樹連線起來,使得現錢在考慮完左子樹後能再供右子樹分配。這樣,我們所想的實際上就是乙個線形關係。

關於樹的線形關係,我們立刻想到樹的遍歷序。

並且由於需要先處理父節點,從直覺上我們先選擇前序遍歷序。

這樣,我們得到了乙個線形關係。

注意樹的前序遍歷序並不能完全表示一棵樹的結構。

我們需要記下每個節點的子孫節點總數childnum。

這時,我們看到了很美好的景像:

如我們所願,樹的結構成了 根—>左子樹—>右子樹 這樣的線形結構。

假設我們將前序遍歷序儲存在list裡面。

我們定義動態規劃的狀態:

f[a][b]表示從a到n總共花費b元所得的最大得分。

則我們有轉移方程:

f[a][b]=max;

即:列舉買不買a節點所代表的物品。如果買的話則轉到線形表的下乙個結點(a節點的後繼),否則a節點所在的子樹就不用討論了,直接將錢轉移到以a為根的子樹的後繼上去。

這樣,我們就不必給子樹分錢了,而是所有的子樹共同分享剩下的錢。

根據前面的思考,我們先依次求出每棵樹的先根遍歷序,並儲存在同乙個序列list中。

為了利用上面的結論,我們還要求出以節點i為根的子樹的節點總數count[i]。

現在我們來設計動態規劃演算法:

定義:

cost[i]表示第i個物品的**

weight[i]表示第i個物品的權值

f[i][j]表示從第i個物品到第n個物品,最多花費j元,能得到的最大權值和。

狀態轉移:

對於乙個節點,我們考慮是否購買它:

購買:那麼我們繼續考慮它後面的節點

不購買:那麼我們跳過它的子孫節點

方程如下:

f[i][j]=max

這個演算法依然是o(nm)的,很完美!

先序遍歷用於優化樹形揹包問題

問題描述 什麼是樹形揹包?簡單的說就是在一棵有 個節點的樹上,每個節點都相當於一件物品,每件物品都有乙個 和乙個得分對於物品a,設其父節點為father a 那麼你必須購買了father a 後才能購買a。現給你m元現金,要求求乙個購買方案,使得物品的總得分最大。我們見過很多此類題目 重修道路 技能...

求先序遍歷

如下 include struct treenode treenode binarytreefromorderings char inorder,char aftorder,int length treenode node new treenode 新建乙個樹節點 node elem aftorde...

根據後序和中序遍歷輸出先序遍歷 (25 分)

本題要求根據給定的一棵二叉樹的後序遍歷和中序遍歷結果,輸出該樹的先序遍歷結果。第一行給出正整數n 30 是樹中結點的個數。隨後兩行,每行給出n個整數,分別對應後序遍歷和中序遍歷結果,數字間以空格分隔。題目保證輸入正確對應一棵二叉樹。在一行中輸出preorder 以及該樹的先序遍歷結果。數字間有1個空...