题目描述
给一棵n 个结点的有根树,结点由1 到n 标号,根结点的标号为1。每个结点上有一个物品,第i 个结点上的物品价值为vi。
你需要从所有结点中选出若干个结点,使得对于任意一个被选中的结点,其到根的路径上所有的点都被选中,并且选中结点的个数不能超过给定的上限lim。在此前提下,你需要最大化选中结点上物品的价值之和。
求这个最大的价值之和。
树形依赖
首先把树线性化,就是弄成dfs序。
然后在序列上做dp,在i这个位置,选了就是i+1,不选就是i+size[i]。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=3000+10,inf=500000000;
int f[maxn][maxn];
int h[maxn],go[maxn*2],next[maxn*2],nfd[maxn],size[maxn],a[maxn];
int i,j,k,l,t,n,m,tot,top,ans;
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void dfs(int x,int y){
nfd[++top]=x;
int t=h[x];
while (t){
if (go[t]!=y){
dfs(go[t],x);
size[x]+=size[go[t]];
}
t=next[t];
}
size[x]++;
}
int main(){
freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,1,n-1){
scanf("%d%d",&j,&k);
add(j,k);add(k,j);
}
dfs(1,0);
fo(i,0,n+1)
fo(j,0,m+1)
f[i][j]=-inf;
f[1][1]=a[nfd[1]];
fo(i,1,n){
if (i==n){
fo(j,0,m)
f[i+1][j]=max(f[i+1][j],f[i][j]);
break;
}
fo(j,0,m)
if (f[i][j]!=-inf){
if (j<m) f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+a[nfd[i+1]]);
f[i+size[nfd[i+1]]][j]=max(f[i+size[nfd[i+1]]][j],f[i][j]);
}
}
fo(i,0,m) ans=max(ans,f[n+1][i]);
printf("%d\n",ans);
}