树形背包dp P2014 [CTSC1997] 选课

链式前向星:链式前向星 详解_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啦!

猜你喜欢

转载自blog.csdn.net/LarsGyonX/article/details/125729295
今日推荐