題目描述
有一棵蘋果樹,如果樹枝有分叉,一定是分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 2 1 3 1 1 4 10 2 3 20 3 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;//遞歸到最上層的根節點1 29 for(int j=min(ans,m);j>=1;--j)//因為有限制枝條的數目,取個min 30 { 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保留多少枝條最優進行dp 35 在這里好好說明下,因為建樹是我們是按照遞歸建的樹。 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