洛咕
题意:有一棵N个节点的二叉树,树根为1,现只能保留m根树枝,每根树枝连接两个节点,且有权值.求能够得到的最大权值和是多少.
分析:首先保留下来的树枝必须还是一棵树(可以不是二叉树),且树根节点必须保留.树上DP擅长于对节点的操作,于是保留m根树枝等价于保留m+1个节点.
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int n,m;
int w[105][105],f[105][105],val[105],l[105],r[105];
void dfs1(int u){
for(int i=1;i<=n;i++){//构建左子树
if(w[u][i]>=0){
val[i]=w[u][i];
//把边u-->i上的权值赋给节点i
l[u]=i;
//记录它的左儿子
w[u][i]=w[i][u]=-1;
//做个标记,不再被选
dfs1(i);
//递归处理左子树
break;
//break很关键,对于节点u它要么有两个儿子,要么没儿子
//此时我们已经找到它的左儿子,那么一定要break掉
//才能进入下面的循环,找到右儿子.
}
}
for(int i=1;i<=n;i++){//构建右子树
if(w[u][i]>=0){
val[i]=w[u][i];
r[u]=i;
w[u][i]=w[i][u]=-1;
dfs1(i);
break;
}
}
return;
}
//以x为根的树上,保留y个节点获得的最大权值和
int dfs2(int x,int y){
if(y==0)return 0;//一个都不能选,自然为0
if(l[x]==0&&r[x]==0)return val[x];
//如果没有左子树和右子树,就只能选自己获得最大权值.
if(f[x][y])return f[x][y];//记忆化
for(int i=0;i<=y-1;i++){//枚举保留节点数量
f[x][y]=max(f[x][y],dfs2(l[x],i)+dfs2(r[x],y-1-i)+val[x]);
}
return f[x][y];
}
int main(){
n=read();m=read();m++;
memset(w,-1,sizeof(w));
for(int i=1;i<n;i++){
int a=read(),b=read(),c=read();
w[a][b]=w[b][a]=c;//数据小,直接邻接矩阵
}
dfs1(1);//建树
printf("%d\n",dfs2(1,m));
return 0;
}