標籤:
題目描述
有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有只有1個兒子的結點)
這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。
我們用一根樹枝兩端串連的結點的編號來描述一根樹枝的位置。下面是一顆有4個樹枝的樹
2 5 \ / 3 4 \ / 1
現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。
給定需要保留的樹枝數量,求出最多能留住多少蘋果。
輸入輸出格式
輸入格式:
第1行2個數,N和Q(1<=Q<= N,1<N<=100)。
N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的資訊。
每行3個整數,前兩個是它串連的結點的編號。第3個數是這根樹枝上蘋果的數量。
每根樹枝上的蘋果不超過30000個。
輸出格式:
一個數,最多能留住的蘋果的數量。
輸入輸出範例
輸入範例:
5 21 3 11 4 102 3 203 5 20
輸出範例:
21
對於樹狀dp,就是在樹上面做動態規劃。關鍵點是樹的層次性,而層次性又是有遞迴的建樹而實現的。要注意這題是有根樹,根節點給定是1,而且必須保留!
題解寫到注釋裡面了
代碼如下:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define inf 0x3f3f3f3f 5 #define M 5000 6 int next[M],pre[M],last[M],apple[M],dp[M][M],n,m,tot=0; 7 /* 8 dp[i][j]表示節點i保留j個枝條的所剩蘋果最大值 9 apple[i]表示第i條邊上的蘋果數10 next,pre,last是用來建邊的數組11 tot來統計邊的序號12 */13 void cnct (int u,int v,int w)14 {15 tot++;16 next[tot]=v;17 pre[tot]=last[u];18 last[u]=tot;19 apple[tot]=w;20 }21 int dfs (int u,int father)22 {23 int ans=0;//ans表示u節點的子節點數目24 for (int i=last[u];i!=0;i=pre[i])25 {26 int v=next[i],value=apple[i];27 if(v == father)continue;//如果下一個相鄰節點就是父節點,則證明到底層了,開始遞迴父節點的兄弟節點28 ans+=dfs(v,u)+1;//遞迴到最上層的根節點129 for(int j=min(ans,m);j>=1;--j)//因為有限制枝條的數目,取個min30 {31 for(int k=min(j,ans);k>=1;--k)32 dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k-1]+value);33 /*34 對於u節點下的子節點j,對j保留多少枝條最優進行dp35 在這裡好好說明下,因為建樹是我們是按照遞迴建的樹。36 進行比較時,dp[u][j]都是前面選擇除i外的子節點得到的最優解結果37 所以dp的時候不可能重複或者漏掉某節點38 */39 }40 }41 return ans;42 }43 int main()44 {45 //freopen("de.txt","r",stdin);46 memset(last,0,sizeof last);47 memset(next,0,sizeof next);48 memset(pre,0,sizeof pre);49 memset(dp,0,sizeof dp);50 scanf("%d%d",&n,&m);51 for(int i=1;i<n;++i)52 {53 int x,y,value;54 scanf("%d%d%d",&x,&y,&value);55 cnct(x,y,value);56 cnct(y,x,value);57 }58 dfs(1,0);59 printf("%d\n",dp[1][m]);60 return 0;61 }62
洛穀P2015 二叉蘋果樹(樹狀dp)