0713-浅谈树形dp-典型例题二

好啦好啦,终于发起了,绝对是机房的电脑跟我有仇,哼(¬︿̫̿¬☆)

类型二:普通树上的dp(上一篇讲的是二叉树,比较容易,如有遗忘戳一戳

普通树和二叉树的区别是啥子嘞,

就是说二叉树里一个结点下最多只有两个儿子结点,所以状态比较好转移(从儿子向上走)。但普通树就不止两个了,可能有多个,那就不好玩了╭(╯^╰)╮。可是,我们可以把普通树转成二叉树啊,一样的dp,熟悉的感觉~

转化的口诀(原则):左儿子,右兄弟

就是说当前结点的第一个儿子,放在它的左子树,其余的儿子挂在第一个儿子的右子树上。

那么对于每个结点而言,它左边的才是它真正的儿子,右边都是它兄弟(同一个父亲)

(大家凑合看看,然后自己试着转转)

如何检验是否转对了?

1.每个结点的信息没有改变(比如其父亲结点,儿子结点,还有的就因题而异了)

2.可以从二叉树再转回去(基本上这条如果做到了,就没什么大问题了)

那么转化的思想get到了,怎么具体用代码实现呢?

先不急,我们等会儿结合着典型例题来看吧

 

 

普通树已经转成二叉树了,接下来就好办多了

!上例题!

洛谷上不晓得有没有,应该有,反正我把这道题放出来,以后类似的题就不虚啦

选课(有点像有依赖的背包)

 

题目描述

学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了 N(N<300)门的选修课程,每个学生可选课程的数量 M 是给定的。学生选修了这M门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。 例如:

课号先修课号学分
11
211
323
43
524

表中 1 是 2 的先修课,2 是 3、4 的先修课。如果要选 3,那么 1 和 2 都一定已被选修过。

你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

输入格式

输入文件的第一行包括两个整数 N、M(中间用一个空格隔开)其中 1≤N≤300,1≤M≤N。
以下N行每行代表一门课。课号依次为 1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过 10 的正整数。

输出格式

输出文件只有一个数,实际所选课程的学分总数。

样例数据 1

输入  


 

7 4 
2 2 
0 1 
0 4 
2 1 
7 1 
7 6 
2 2

输出

13

每门课的直接先修课最多只有一门。”这句话告诉我们每门科只会对应一个父亲

两门课也可能存在相同的先修课。"这是在说明这是一棵普通树,每个节点下不一定只有两个儿子

这种无先修课的就是根节点啦,而无先修课的课不止一门,所以这是一个森林~(但和普通树转二叉树没什么区别,直接选中一棵树的根节点作为森林的根节点,然后左儿子,右兄弟)

然后对于每个节点而言,如果选他的左儿子,那么该节点必选,但如果选他的右儿子,就不一定要选根了(因为他们是兄弟关系呗)根据这个来状态转移,就和例题一类似了

下面是小蒟蒻自己想了然后敲的,转麻烦了(;′⌒`),大家尽可以忽略不看,但要提醒一点,也是本宝宝gg的地方。不要用0号节点来作为虚拟的根节点,会和空节点混淆的,用n+1都可以

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m;
struct node{
	int fa,son[305],num,v;
	int lc,rc;
}a[309];
void build(int k){
	int i,j,now;
	int que[3009],qn;
	que[qn=1]=k;
	for(j=1;j<=qn;++j){
		int kk=que[j];
		now=a[kk].lc=a[kk].son[1];
		if(now) que[++qn]=now;
		for(i=2;i<=a[kk].num;++i){
			a[now].rc=a[kk].son[i];
			now=a[now].rc;
			que[++qn]=now;
		}
	}
}
int f[309][309];
int dp(int i,int j){
	if(j==0||i==0) return 0;
	if(a[i].lc==0&&a[i].rc==0) return a[i].v;
	if(f[i][j]) return f[i][j];
	if(a[i].rc) f[i][j]=dp(a[i].rc,j);
	
	for(int k=0;k<j;++k)
			f[i][j]=max(f[i][j],dp(a[i].lc,k)+dp(a[i].rc,j-k-1)+a[i].v);
	
	return f[i][j];
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k;
	memset(a,0,sizeof(a));
	for(i=1;i<=n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		a[i].fa=x;
		a[i].v=y;
		a[x].num++;
		a[x].son[a[x].num]=i;
	}
	a[0].v=0;
	build(0);
	
	printf("%d",dp(a[0].lc,m));
	return 0;
}

下面放上ldw老师的,没有对比就没有伤害(让我去墙角哭一会儿)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,son[306],f[309][309];
struct node{
	int lc,rc,v;
}a[309];
int dp(int i,int j){
	if(i==0||j==0) return 0;
	if(a[i].lc==0&&a[i].rc==0) return a[i].v;
	if(f[i][j]) return f[i][j];
	if(a[i].rc) f[i][j]=dp(a[i].rc,j);
	for(int k=0;k<j;++k)
		f[i][j]=max(f[i][j],dp(a[i].lc,k)+dp(a[i].rc,j-k-1)+a[i].v);
	return f[i][j];
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k;
	for(i=1;i<=n;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		a[i].v=y;
		if(!son[x]){//如果x还没有儿子 
			a[x].lc=i;
		}
		else  a[son[x]].rc=i;//左儿子,右兄弟 
		son[x]=i;
	}
	
	printf("%d",dp(a[0].lc,m)); 
	return 0;
}		if(!son[x]){//如果x还没有儿子 
			a[x].lc=i;
		}
		else  a[son[x]].rc=i;//左儿子,右兄弟 
		son[x]=i;
	}
	
	printf("%d",dp(a[0].lc,m)); 
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值