题意
n
n
门功课形成一棵树,每门课有一个学分,选 门,选择一门课的前提是选择它的父亲,求最大学分。
1≤n,m≤300
1
≤
n
,
m
≤
300
思路
这是一个树上的依赖背包问题。首先考虑暴力,设
dpi,j
d
p
i
,
j
为
i
i
这棵子树,选 门课时的最大学分,那将每棵子树当一个泛化物品,那么对于一个子树
k
k
,有如下方程:
复杂度不难发现是
O(nm2)
O
(
n
m
2
)
的。
其实树上依赖背包问题有一个优化,我们从一般的背包开始分析:
对于
n
n
个物品,总钱数为 。第
i
i
个物品费用为 ,价值为
vi
v
i
。设
dpi,j
d
p
i
,
j
表示第
i
i
个物品到第 个物品,花费为
j
j
的最大收入,复杂度 。在树上,是否也可以套用这个思路呢?
我们也用
dpi,j
d
p
i
,
j
表示
[i,n]
[
i
,
n
]
号节点,费用为
j
j
时的最大价值。对于一个节点 ,选
j
j
门课,讨论的问题莫过于是 节点选与不选,如果选,就转化成了它子树中的问题,如果不选,就转化成了从遍历完子树后第一个到的点开始的问题。直觉告诉我们,应该处理一个
dfs
d
f
s
序(前序遍历的顺序),并处理每棵子树的大小(以“跳”出子树)。我们用
orii
o
r
i
i
表示
dfs
d
f
s
序为
i
i
的节点的原编号,有如下方程:
其中
dp
d
p
的节点编号是
dfs
d
f
s
序的编号。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 303
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N>G;
int dp[N][N],dfn[N],ori[N],sz[N],ord;
int n,m,p[N];
void dfs(int u)
{
dfn[u]=++ord,ori[ord]=u,sz[u]=1;
EOR(i,G,u)
{
int v=G.to[i];
dfs(v);
sz[u]+=sz[v];
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
G.clear();
memset(dp,0,sizeof(dp));
FOR(v,1,n)
{
int u;
scanf("%d%d",&u,&p[v]);
G.add(u,v);
}
m++;n++;ord=0;
dfs(0);
DOR(i,n,1)
FOR(j,1,m)
dp[i][j]=max(dp[i+1][j-1]+p[ori[i]],dp[i+sz[ori[i]]][j]);
printf("%d\n",dp[1][m]);
}
return 0;
}