版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/87903421
Problem
题目描述
有一棵苹果树,如果树枝有分叉,一定是分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个。
输出格式
一个数,最多能留住的苹果的数量。
题解
这道题就是一道典型的二维树形DP问题。
设 表示在以 为根的子树中,保留了 个枝条所保留的最大苹果。
状态转移方程:
相信状态转移方程十分清楚明白,这里的 显然是先前已经枚举过的其它子树。
我们此时需要求出i和j的取值范围,通过仔细画图可以得知:
注意状态转移方程
和
需要逆序枚举,原理同
,
是同一棵子树的转移。
代码如下:
#include<bits/stdc++.h>
#define Mp make_pair
using namespace std;
int n,q;
int size[200];
vector< pair<int,int> >a[200];
int f[200][200];
void getsize(int x,int fa)
{
size[x]=1;
for (int i=0;i<a[x].size();++i)
{
int y=a[x][i].first;
if (y == fa) continue;
getsize(y,x);
size[x]+=size[y];
}
}
void dp(int x,int fa)
{
for (int k=0;k<a[x].size();++k)
{
int y=a[x][k].first,val=a[x][k].second;
if (y == fa) continue;
dp(y,x);
for (int i=min(size[x]-1,q);i>=1;--i)
for (int j=min(size[y]-1,i-1);j>=0;--j)
f[x][i]=max(f[y][j]+val+f[x][i-j-1],f[x][i]);
}
}
int main(void)
{
freopen("APPLE.in","r",stdin);
freopen("APPLE.out","w",stdout);
scanf("%d %d",&n,&q);
for (int i=1,x,y,v;i<n;++i)
{
scanf("%d %d %d",&x,&y,&v);
a[x].push_back(Mp(y,v));
a[y].push_back(Mp(x,v));
}
getsize(1,0);
dp(1,0);
printf("%d",f[1][q]);
return 0;
}