题目链接
题意:
给你一个森林,有
n
n
个点,每个点有一个价值,能选某个点的前提是它的所有祖先节点都被选,让你求只能选个节点的最大价值。
题解:
首先先把所有没有父节点的点向
0
0
号点连边,让森林变成一棵树,然后我们由叶子向根dp,设为在根节点为
i
i
的子树里选个节点的最大获益。当
j=0
j
=
0
时,
dp[i][j]=0
d
p
[
i
]
[
j
]
=
0
。当
j≠0
j
≠
0
时,设
y∈son[x]
y
∈
s
o
n
[
x
]
,
c∈[1,n]
c
∈
[
1
,
n
]
这可以转化为一个分组背包,每个子树看作一组,从每组中至多选一个物品,体积为 j j ,价值为,这样对这个树形结构进行背包dp的转移就行。
复杂度 O(n3) O ( n 3 ) ,注意 0 0 号点是个虚拟节点,实际上不需要被选,就是最终答案。
洛谷2014选课代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,son[310][310],dp[310][310],val[310],cnt[310];
void dfs(int x)
{
for(int i=1;i<=cnt[x];++i)
{
int y=son[x][i];
dfs(y);
for(int j=m;j>=0;--j)
{
for(int k=j;k>=0;--k)
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]);
}
}
if(x!=0)
{
for(int i=m;i>=1;--i)//注意倒叙枚举
dp[x][i]=dp[x][i-1]+val[x];//x本身需要占一门课
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
int x;
scanf("%d%d",&x,&val[i]);
son[x][++cnt[x]]=i;
}
dfs(0);
printf("%d\n",dp[0][m]);
return 0;
}