题面
一本通上树形动规的第一道例题,本质上是一个背包问题
用 fu, i 表示在第 u 棵子树上,选取 i 条边时能获得的最大价值
那么就有状态转移方程:
fu, i=max(fu, i,fu, i−j−1+fv, j+e[i].val)
对于结点 u,dfs枚举它的每一棵子树 v。 fu, i 为 u 结点选择 i 条边的价值。它可以用 v 结点选择 j 条边的价值加上 v 结点先前选的 i−j−1 条边的价值来更新。减去1是因为一旦选择了子树 v,那么 u 与 v 相连的边也要选中。
接着我们就可以根据以上这些写出代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <cstdio> #include <algorithm> using namespace std; const int maxn=103; int n,m,cnt,head[maxn],sz[maxn],f[maxn][maxn<<1]; struct Edge{ int to,val,nxt; }a[maxn<<1]; void add(int x,int y,int z) {a[++cnt]=(Edge){y,z,head[x]}; head[x]=cnt;} void dfs(int x,int fa){ for(int i=head[x];i;i=a[i].nxt){ if(a[i].to==fa) continue; dfs(a[i].to,x); sz[x]+=sz[a[i].to]+1; for(int j=min(sz[x],m);j>=0;j--){ for(int k=min(sz[a[i].to],j-1);k>=0;k--) f[x][j]=max(f[x][j],f[x][j-k-1]+f[a[i].to][k]+a[i].val); } } } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y,z;i<=n-1;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } dfs(1,0); printf("%d\n",f[1][m]); return 0; }
|