洛谷P2015 二叉苹果树 题解
题目传送门。
一眼树形 DP。
首先设 \(f_{i,j}\) 表示在 \(i\) 这个子树中保留 \(j\) 条边,那么状态转移为:
\[f_{i,j} = f_{i,j-k-1}+f_{son,k}+w
\]
\(son\) 表示 \(i\) 的儿子,\(w\) 表示对应边权。
那么这个时候你会问了,为什么是 \(j-k-1\) 而不是 \(j-k\) 呢?因为 \(i\) 到 \(son\) 这条边不需要在这里算。至于为什么不能把 \(w\) 删掉,然后改成 \(j-k\) 是因为这里对 \(son\) 这个儿子还没有处理好,不能直接拿来用。
你可能会问这 \(f_{i,j-k-1}\) 没有算全吧?\(son\) 不一定是最后一个遍历的儿子啊。其实这样不会影响,因为如果这次没算到最优答案,在后面也一定会算到之前应该得到的最优答案。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int f[N][N];
int d[N];
struct node
{
int x;
int w;
};
vector<node>e[N];
int n,m;
void dfs(int x,int fa)
{
for(auto v:e[x])
{
if(v.x!=fa)
{
dfs(v.x,x);
d[x]+=d[v.x]+1;//子树枝干数量计算
for(int i = min(d[x],m);i;i--)
{
for(int j = min(d[v.x],i-1);j>=0;j--)
{
f[x][i] = max(f[x][i],f[x][i-j-1]+f[v.x][j]+v.w);
}
}
}
}
}
signed main()
{
scanf("%d %d",&n,&m);
for(int i = 1;i<n;i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
e[x].push_back({y,z});
e[y].push_back({x,z});
}
dfs(1,0);
printf("%d",f[1][m]);
return 0;
}