题解
由根分成左子树和右子树的情况
需要保留q条边 也就是要保留j=q+1个点(必须包括根节点)
如果树根的两棵子树都为非空,设左子树保留k个,则右子树保留(j-1)-k个节点。
注:j-1是因为要包括根节点本身
状态:f[i][j]表示以i为根 保留含自己的共j个点最多能留住的苹果数。
转移方程:
f[i][j]=max(f[l[i]][k]+f[r[i]][j-1-k]+a[i]};
#include<bits/stdc++.h>
using namespace std;
int n,q,mp[101][101],l[101],r[101],a[101],f[101][101];
void maketree(int fa)
{
for(int i=1;i<=n;i++)
if(mp[fa][i]>=0)
{
l[fa]=i;
a[i]=mp[fa][i];
mp[fa][i]=-1;
mp[i][fa]=-1;
maketree(i);
break;//really imp 找到没有后开始找右儿子
}
for(int i=1;i<=n;i++)
if(mp[fa][i]>=0)
{
r[fa]=i;
a[i]=mp[fa][i];
mp[fa][i]=-1;
mp[i][fa]=-1;
maketree(i);
break;
}
}
int dp(int fa,int num)//f[i][j]是指以i为根 保留含自己的共j个点最多能留住的苹果数
{
if(num==0) return 0;
if((l[fa]==0)&&(r[fa]==0)) return a[fa];
//到了叶子节点 直接返回a[fa](a[i]表示i指向其父亲的枝的苹果树)
if(f[fa][num]>0) return f[fa][num];
//记忆化搜索 如果已经搜到了就直接返回
for(int k=0;k<=num-1;k++)
f[fa][num]=max(f[fa][num],dp(l[fa],k)+dp(r[fa],num-1-k)+a[fa]);
//左儿子保留k个点 有儿子保留(num-1)-k个点
return f[fa][num];
}
int main()
{
scanf("%d%d",&n,&q);
q++;//保留q个边相当于保留q+1个点
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mp[i][j]=-1;//初始化 区分有0个or没有枝
for(int i=1;i<=n-1;i++)
{
int u,v,val;
scanf("%d%d%d",&u,&v,&val);
mp[u][v]=val;//不确定方向
mp[v][u]=val;
}
maketree(1);//以1为根建立二叉树
printf("%d",dp(1,q));
}