之前听过思路,胡搞了一下……
左儿子右兄弟写起来简单些(枚举次数并不少)
转移:
dp[i][j]为到第i个点剩j人的最大可获价值
节点上留人则可由左儿子和右兄弟的和+自己转来;
节点上不留人只能由右兄弟转来
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 200000 + 50;
int head[MAXN],next[MAXN << 1],tot;
struct edge
{
int f,t;
}l[MAXN << 1];
int w[MAXN],a,b,L[MAXN],R[MAXN];
void init(int n)
{
for(int i = 0;i <= n;i ++)
head[i] = -1,R[i] = -1,L[i] = -1;
}
int n,m;
void build(int f,int t)
{
l[++ tot] = (edge){f,t};
next[tot] = head[f];
head[f] = tot;
}
void solve(int u)
{
if(head[u] == -1)return;
int i = head[u];
int p = l[i].t;
L[u] = p;
solve(p);
for(i = next[i];i != -1;i = next[i])
{
R[p] = l[i].t;
p = R[p];
solve(p);
}
}
int dp[1005][1005];
int dfs(int u,int sy)
{
if(u == -1)return 0;
if(!sy)return dp[u][sy] = 0;
if(dp[u][sy])return dp[u][sy];
int ans1 = 0,ans2 = 0;
for(int i = 0;i <= sy - 1;i ++)
{
ans1 = max(ans1,dfs(L[u],i) + dfs(R[u],sy - 1 - i));
}
ans1 += w[u];
for(int i = 0;i <= sy;i ++)
{
ans2 = max(ans2,dfs(R[u],sy - i));
}
return dp[u][sy] = max(ans1,ans2);
}
int main()
{
scanf("%d%d",&n,&m);
init(n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&w[i]);
}
for(int i = 1;i <= n;i ++)
{
scanf("%d%d",&a,&b);
build(a,b);
}
solve(0);
printf("%d",dfs(L[0],m));
return 0;
}