題目描述
有一棵蘋果樹,如果樹枝有分叉,一定是分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個。
輸出格式:一個數,最多能留住的蘋果的數量。
輸入輸出樣例
輸入樣例#1:
5 2 1 3 1 1 4 10 2 3 20 3 5 20
輸出樣例#1:
21
這是一道比較不錯的樹形DP的題目,
剛開始的時候用貪心亂搞,搞了搞邊,搞了搞點,搞了顆樹,搞了39分。。。
貪心
剛開始的時候用貪心亂搞,搞了搞邊,搞了搞點,搞了顆樹,搞了39分。。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int MAXN=1001; 7 int n,q; 8 int v[MAXN]; 9 int vis[MAXN]; 10 int deep[MAXN]; 11 int read(int & n) 12 { 13 char p='+';int x=0; 14 while(p<'0'||p>'9') 15 p=getchar(); 16 while(p>='0'&&p<='9') 17 x=x*10+p-48,p=getchar(); 18 n=x; 19 } 20 struct node 21 { 22 int u; 23 int v; 24 int w; 25 int nxt; 26 }edge[MAXN]; 27 int num=1; 28 int head[MAXN]; 29 void add_edge(int x,int y,int z) 30 { 31 edge[num].u=x; 32 edge[num].v=y; 33 edge[num].w=z; 34 edge[num].nxt=head[x]; 35 head[x]=num++; 36 } 37 struct deal 38 { 39 int how; 40 int val; 41 int l; 42 }a[MAXN]; 43 void build_tree(int p) 44 { 45 vis[p]=1; 46 for(int i=head[p];i!=-1;i=edge[i].nxt) 47 { 48 if(vis[edge[i].v]==0) 49 { 50 deep[edge[i].v]=deep[p]+1; 51 build_tree(edge[i].v); 52 } 53 54 } 55 } 56 void deal_val(int p,int pre) 57 { 58 vis[p]=1; 59 for(int i=head[p];i!=-1;i=edge[i].nxt) 60 { 61 int will=edge[i].v; 62 if(will!=0&&vis[will]==0&&deep[will]>deep[p]) 63 { 64 a[pre].l++; 65 a[pre].val+=edge[i].w; 66 deal_val(will,pre); 67 } 68 69 70 } 71 } 72 int comp(const deal & a ,const deal & b) 73 { 74 if(a.val!=b.val) 75 return a.val<b.val; 76 return edge[head[a.how]].w<edge[head[b.how]].w; 77 } 78 int main() 79 { 80 int ans=0; 81 read(n);read(q); 82 q=n-q; 83 for(int i=1;i<=n;i++) 84 head[i]=-1,a[i].how=i; 85 for(int i=1;i<=n-1;i++) 86 { 87 int x,y,z; 88 read(x);read(y);read(z); 89 ans=ans+z; 90 add_edge(x,y,z); 91 add_edge(y,x,z); 92 } 93 deep[1]=1; 94 build_tree(1); 95 for(int i=1;i<=n;i++) 96 { 97 memset(vis,0,sizeof(vis)); 98 deal_val(i,i); 99 } 100 101 sort(a+1,a+n+1,comp); 102 103 for(int i=1;i<=q-1;i++) 104 ans=ans-edge[head[a[i].how]].w; 105 cout<<ans; 106 return 0; 107 }
後來看題解用樹形DP,
對於每一個節點,我們可以列舉它相連的邊,
用dp[i][j]表示第i個節點,在他的子節點中保留j條樹枝的最大值
那麼我們可以列舉節點和j,當然,在列舉j的時候我們需要同時列舉一個k
我們可以想一下,該節點i需要保留j條邊,那麼我們取他的最大值得時候,可以對他相連的節點進行DP,
如果j=5,這時候需要保留5條邊,
因為整個樹是二叉樹,所以說,他的相連的一條邊所能刪除的最大數就是4(相連的線段一定會刪除)
那麼k的範圍:0--4
動態轉移方程:
dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[will][k]+edge[i].w);
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int MAXN=1001; 7 int n,q; 8 int v[MAXN]; 9 int vis[MAXN]; 10 int deep[MAXN]; 11 int dp[MAXN][MAXN]; 12 int read(int & n) 13 { 14 char p='+';int x=0; 15 while(p<'0'||p>'9') 16 p=getchar(); 17 while(p>='0'&&p<='9') 18 x=x*10+p-48,p=getchar(); 19 n=x; 20 } 21 struct node 22 { 23 int u; 24 int v; 25 int w; 26 int nxt; 27 }edge[MAXN]; 28 int num=1; 29 int head[MAXN]; 30 void add_edge(int x,int y,int z) 31 { 32 edge[num].u=x; 33 edge[num].v=y; 34 edge[num].w=z; 35 edge[num].nxt=head[x]; 36 head[x]=num++; 37 } 38 int dfs(int u,int fa) 39 { 40 int ans=0; 41 for(int i=head[u];i!=-1;i=edge[i].nxt) 42 { 43 int will=edge[i].v; 44 if(will==fa) 45 continue; 46 ans+=dfs(will,u)+1; 47 for(int j=min(q,ans);j>=1;j--) 48 { 49 for(int k=min(j,ans)-1;k>=0;k--) 50 { 51 dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[will][k]+edge[i].w); 52 } 53 } 54 } 55 return ans; 56 } 57 int main() 58 { 59 read(n);read(q); 60 for(int i=1;i<=n;i++) 61 head[i]=-1; 62 for(int i=1;i<=n-1;i++) 63 { 64 int x,y,z; 65 read(x);read(y);read(z); 66 add_edge(x,y,z); 67 add_edge(y,x,z); 68 } 69 dfs(1,0); 70 cout<<dp[1][q]; 71 return 0; 72 }