週中訓練筆記19 樹形DP總結

2021-08-11 08:13:54 字數 3205 閱讀 2914

樹形dp暫時告一段落,今天總結了一下:

首先,樹形dp=樹+dp,是一種二維的dp題目,也可以結合揹包問題,因為子樹的數量不確定,所以一般用vector來儲存子節點或子樹個數。

平時作的動態規劃都是線性的或者是建立在圖上的,線性的動態規劃有二種方向既向前和向後,相應的線性的動態

規劃有二種方法既順推與逆推,而樹型動態規劃是建立在樹上的,所以也相應的有二個方向: 

1、葉->根:在回溯的時候從葉子節點往上更新資訊

2、根 - >葉:往往是在從葉往根dfs一遍之後(相當於預處理),再重新往下獲取最後的答案。

先看一道簡單的例題:

poj 2342 anniversary party   先來個題入門一下~

題意:某公司要舉辦一次晚會,但是為了使得晚會的氣氛更加活躍,每個參加晚會的人都不希望在晚會中見到他的直接上司,現在已知每個人的活躍指數和上司關係(當然不可能存在環),求邀請哪些人(多少人)來能使得晚會的總活躍指數最大。

解題思路:

任何乙個點的取捨可以看作一種決策,那麼狀態就是在某個點取的時候或者不取的時候,以他為根的子樹能有的最大活躍總值。分別可以用f[i,1]和f[i,0]表示第i個人來和不來。

當i來的時候,dp[i][1] += dp[j][0];//j為i的下屬

當i不來的時候,dp[i][0] +=max(dp[j][1],dp[j][0]);//j為i的下屬

[cpp]view plain

copy

#include

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;  

#define maxn 6005

intn;  

intdp[maxn][2],father[maxn];

//dp[i][0]0表示不去,dp[i][1]1表示去了

bool

visited[maxn];  

void

tree_dp(

intnode)  

}  }  int

main()  

root = 0;//記錄父結點

bool

beg = 1;  

while

(scanf(

"%d %d"

,&c,&f),c||f)  

}  while

(father[root])

//查詢父結點

root=father[root];  

tree_dp(root);  

intimax=max(dp[root][0],dp[root][1]);  

printf("%d\n"

,imax);  

}  return

0;  

}  

這跟普通的dp一樣,只不過擴充套件到了二維,只要找到狀態轉移方程,再用遞迴逐個算出每個子節點所對應的權值,就ok啦!

接下來再看一道結合揹包問題的樹形dp:

0-1揹包裸**:

for i=1..n

for v=v..0

f[v]=max;

狀態轉移方程:f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]);

m是個數,j是存幾個,f[i][j]表示的是以i為根攻克j個城堡(且這j個城堡必須是它子樹上的,不包括它本身),dp[i][j]表示的是是以i為根攻克j個城堡(且這j個城堡必須是它子樹上的,一定它本身,ans[i]表示每個城堡的寶物,所以一定有dp[i][1]=ans[i];)。

for(int k=m;k>=0;k--)

for(int j=0;j<=k;j++)

f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]);

更新f[root][0~m]陣列,然後全部更新完之後更新dp[root][0~m]。

如圖所示樣例,先從root即0點訪問3,3沒有孩子,執行更新dp操作

,因為所以葉子都滿足dp[i][0~m]=ans[i],所以dp[3][0~m]都等於ans[3],以下同理。

返回到root,更新f[0][m~0]。

訪問root-->2-->7-->6,訪問到葉子,更新dp[6][0~m]。返回7,更新f[7][m~0],

從7-->5,更新葉子節點dp[5][0~m],

從5-->7,再次更新f[7][m~0],

從7-->2,更新dp[7][0~m],返回2節點,更新f[2][m~0],

從2-->4,更新葉子節點dp[4][0~m],

從4-->2,更新f[2][m~0],

從2-->1,更新dp[1][0~m],

從1-->2,更新f[2][m~0],

從2-->root,更新dp[2][0~m],

更新f[0][m~0],更新dp[0][0~m]。

[

#include

#include

#define n 205

intn,m,edgenum=0;  

intans[n],dp[n][n],f[n][n];  

intvisit[n],head[n];  

struct

lineedge[n];  

intmax(

inta,

intb)  

void

add(

intu,

intv)  

void

dfs(

introot)  

}  for(

inti=1;i<=m+1;i++)  

dp[root][i]=f[root][i-1]+ans[root];  

}  int

main()  

dfs(0);  

printf("%d\n"

,dp[0][m+1]);  

}  }  

以上只是入門題目,能較簡單的反映樹形dp的基本思想,更複雜的題目說實話能看懂題解就不錯了,現在根本做不出來。。。

還需努力!

週中訓練筆記

rand 函式 rand n 範圍 0 n 1 n rand m n 1 範圍n m 線段樹的概括 1.是乙個完全二叉樹 2.主要用於解決解決連續區間的動態查詢問題 現在對於線段樹的認識還很侷限,所以概括的也非常籠統,接下來的幾天會繼續研究線段樹的。然後就是今天的廣西邀請賽重現賽的題 今天因為下午有...

週中訓練筆記(四)

線段樹總結 線段樹儲存的是區間的資訊然後在可以進行區間的各種操作。對於節點a n 它的左兒子為a 2 n 右兒子為a 2 n 1 假如這個節點所表示的區間為 1,5 那麼它左兒子表示的區間為 1,3 右兒子表示的區間為 4,5 公式為mid l r 2,左兒子表示的區間為 l,mid 右兒子表示的區...

週中訓練筆記11 線段樹總結

線段樹專題接近尾聲了,是時候總結一波了,說來慚愧,線段樹專題差不多都是參照題解才做出來的,雖然知道是套模板但是具體細節真的很拿人啊。主要總結一下線段樹的模板吧,題目的 實現都要以模板為框架構造 首先提出乙個問題 給你n個數,有兩種操作 1 給第i個數的值增加x 2 詢問區間 a,b 的總和是什麼?輸...