链式前向星:链式前向星 详解_ReverieZH的博客-CSDN博客_链式前向星
更适合层次遍历了。edge数据结构和第一层循环遍历都是链式前向星的知识点。
树形dp一般是dfs+dp,这里分析一下题意,在有限的选课数中学分要达到最大,可以抽象成背包,背包容量为选课数,value相当于学分数。
状态转化式如下:
dp[i][j]表示,在根为i的子树中,选择了j门课的总学分数
背包dp的思想是遇到物品,比较选和不选哪个价值更大,这边选课也一样。看选和不选哪个学分多。
初始状态是j=1,就是单选一门课的状态。
本题将0作为根(一门必选课),可以避免连通分支的判断。
代码如下:
#include<stdio.h>
#define N 1000
using namespace std;
//链式前向星的数据结构
struct {
int to,next;
//to:边指向的节点
//nexxt:此边的下一条边
}edge[N];
//head指的是该点指出的第一条边 ,tot指的是边的个数
int head[N],tot,n,m,s[N],dp[N][N];
// 链式前向星加边
void add(int fa,int son)
{
edge[++tot].to=son;
edge[tot].next=head[fa];
head[fa]=tot;
}
//递归+树形背包dp
void dfs(int root)
{
//找到第一条边
int e=head[root];
//遍历根节点的所有分支
for(int i=e;i;i=edge[i].next)
{
//子树深搜
int son=edge[i].to;
//非叶子递归
dfs(son);
//j,选课数
for(int j=m+1;j>=1;j--)
{
//子树选课数
for(int k=0;k<j;k++)
{
//递推式
dp[root][j]=(dp[root][j]>dp[son][k]+dp[root][j-k])?dp[root][j]:dp[son][k]+dp[root][j-k];
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int k;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&k,&s[i]);
add(k,i);
dp[i][1]=s[i];
}
dfs(0);
printf("%d",dp[0][m+1]);
}
最后当然是AC啦!