顾名思义:在树上进行DP
思想: 把每一个节点当成一个包,包套包,以每一个节点的儿子为范围进行dp
例题:

题目描述 有一棵点数为 nn 的树,树边有边权。给你一个在 0 \sim n0∼n 之内的正整数 kk ,你要在这棵树中选择 kk 个点,将其染成黑色,并将其他 的 n-kn−k 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。 输入格式 第一行包含两个整数 n,kn,k。 第二到 nn 行每行三个正整数 fr, to, disfr,to,dis,表示该树中存在一条长度为 disdis 的边 (fr, to)(fr,to)。输入保证所有点之间是联通的。 输出格式 输出一个正整数,表示收益的最大值。 输入输出样例 输入 #1复制 3 1 1 2 1 1 3 2 输出 #1复制 3 说明/提示 对于 100\%100% 的数据,0 \leq n,k \leq 20000≤n,k≤2000。
代码:仔细看,仔细想

#include <bits/stdc++.h> using namespace std; #define ri register int #define M 2005 long long f[M][M]; struct edge{ int val,v; edge (){} edge(int a,int b){v=a,val=b;} }; vector <edge> p[M]; int T;int n,m; int sz[M],vis[M]; void dfs(int u) { sz[u]=1; vis[u]=1; f[u][0]=0;f[u][1]=1;//?? 仔细思考 for(ri i=0;i<p[u].size();i++) { int v=p[u][i].v; if(vis[v])continue ; dfs(v); sz[u]+=sz[v]; for(ri j=min(T,sz[u]);j>=0;j--) { for(ri k=0;k<=min(j,sz[v]);k++) { if(f[u][j-k]==-1) continue ; // 类似于装满问题,很重要,排除不合理的情况; long long val=1ll*p[u][i].val*(T-k)*k+1ll*p[u][i].val*(sz[v]-k)*(n-T-sz[v]+k); // 转化为路径经过几次问题 f[u][j]=max(f[u][j],f[u][j-k]+val+f[v][k]); /// 相当于 把树节点当成一个背包,包套包,以包为单位 } } } } int main(){ scanf("%d%d",&n,&T); for(ri i=1;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); edge t=edge(b,c); p[a].push_back(t); t=edge(a,c); p[b].push_back(t); } memset(f,-1,sizeof(f)); dfs(1); printf("%lld",f[1][T]); return 0; }