树形背包(有依赖性的背包)
由底向上更新,把子树处理成一个最优物品集,即像背包九讲说的一样,对于所以费用相等的,我们用01背包跑一个最大的价值,作为一个新的物品集。而向上更新时,每一个容量对于每一个物品集的选取而言,相当于做一次多重背包,选择一个取法,作为最优解,而这些容量对应的最优解集又会作为一个新的最优物品集回溯上去。
AC代码
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=205;
const int inf=0x3f3f3f3f;
const int mod=1e7+7;
const LL maxn=1e18;
#define fi first
#define se second
#define ls (i<<1)
#define rs ((i<<1)|1)
LL read()
{
LL x=0,t=1;
char ch=getchar();
while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
return x*t;
}
/// f[u][j]=max(f[v][j-k-1]+f[v][k]+w[u][v],f[u][j]);
struct edge
{
int from,to,w,next;
edge(){}
edge(int ff,int tt,int ww,int nn)
{
from=ff; to=tt; w=ww; next=nn;
}
};
edge e[N<<1];
int n,m,tot;
int head[N],f[N][N],num[N];
void add(int from,int to,int w)
{
e[++tot]=edge(from,to,w,head[from]);
head[from]=tot;
}
void dfs(int u,int pre)
{
num[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==pre) continue;
dfs(v,u);
num[u]+=num[v];
}
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to,w=e[i].w;
if(v==pre) continue;
for(int j=min(m,num[u]);j>=0;j--) ///能保留的最多边(两个取min 是为了优化常数,并不是必要的)
for(int k=min(j-1,num[v]);k>=0;k--) /// j-k -1 是因为取了他的子树的边必须要取他与子树根结点的连边
f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+w);
}
}
int main()
{
n=read(); m=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z); add(y,x,z);
}
dfs(1,0);
printf("%d
",f[1][m] );
return 0;
}