Description:
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)。这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。
Analysis:
保留Q个树枝即保留 Q + 1个节点。对于一个节点所在的树,保留j个节点,它自己必须保留,子树分三种情况:①左子树为空,右子树保留j-1个节点。②右子树为空,左子树保留j-1个节点。③左右子树均不为空,左子树保留k个节点,则右子树保留j - k - 1个节点(0 <= k <= j - 1)
dp[i][j] := 以i为根的树中保留j个节点的最大权值和。状态转移方程:dp[i][j] = max{dp[lc[i][k] + dp[rc[i][j-k-1] + a[i]},初始化dp[i][j] = 0,ans = dp[1][Q+1]
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define N 110
using namespace std;
struct edge{
int to,w,next;
}e[N];
int dp[N][N],lc[N],rc[N],a[N],map[N][N],n,q;
inline void add(int u,int v,int w){
}
void build(int k){
// >= 0 !!!!
for(int i = 1;i <= n;++i){
if(map[k][i] >= 0){
lc[k] = i;
a[i] = map[k][i];//?
map[i][k] = map[k][i] = -1;
build(i);
break;
}
}
for(int i = 1;i <= n;++i){
// >= 0 !!!!
if(map[k][i] >= 0){
rc[k] = i;
a[i] = map[k][i];
map[i][k] = map[k][i] = -1;
build(i);
break;
}
}
}
int DP(int i,int j){
if(j == 0) return 0;
else if(dp[i][j] > 0) return dp[i][j];
else if((lc[i] == 0) && (rc[i] == 0)) return a[i];
else if(lc[i] == 0) return dp[i][j] = DP(rc[i],j - 1) + a[i];
else if(rc[i] == 0) return dp[i][j] = DP(lc[i],j - 1) + a[i];
else{
for(int k = 0;k <= j - 1;++k){
dp[i][j] = max(dp[i][j],DP(lc[i],k) + DP(rc[i],j - 1 - k) + a[i]);
}
return dp[i][j];
}
}
void solve(){
build(1);
int ans = DP(1,q);
printf("%d
",ans);
}
int main(){
scanf("%d%d",&n,&q);
++q;
memset(map,-1,sizeof(map));
for(int i = 1;i < n;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
map[x][y] = map[y][x] = z;
}
solve();
return 0;
}