树形DP(动态规划)

文章介绍了如何运用递归和动态规划(DP)方法处理二叉树和多叉树问题,如二叉苹果树的最小代价操作、没有上司的舞会节点选择和树形背包问题。作者详细解释了在树结构上进行DP的原理和步骤,以及如何通过DFS遍历和状态转移来解决问题。
摘要由CSDN通过智能技术生成

代码工具(以二叉苹果树为例)

二叉树

int dfs(int u, int i) {
	if (dp[u][i]) return dp[u][i];  // u 是节点 , i 是变量情况 , 参考具体的例题 
	for (int k = 0; k < j; k++) { // 枚举第二个参数i的分配情况 
		dp[u][i] = max(dp[u][i], dfs(u.lson, k) + dfs(u.rson, j - k));
	}
	return dp[u][i];
} 

多叉树(递归分担)

int dfs(int u, int father) {
	for (int i = 0; i < edge[u].size(); i++) { // 枚举u节点的每一个孩子 (节点) 
		// 得到这个树枝的权值和末顶点
		int v = edge[u][i].v, w = edge[u][i].w;
		// 不回头搜索父亲  因为存多叉树的时候是用vector存无向边的 
		if (v == father) continue; 
		// 递归到最深的叶子节点,然后返回,为了后面的sum做准备 
		dfs(v, u);
		// 统计这个点有多少个边
		sum[u] = sum[v] + 1;
		// 从大边开始遍历到小边(必须)类比背包问题,就是说一个边只能删除一次 
		for (int j = sum[u]; j >= 0; j--) {
			// 遍历
			for (int k = 0; k <= j - 1; k++) {
				// 保留效果要么是以前的,要么是根的其他子树保留j - k - 1条,v子树保留k条。 
				dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k] + w); // w 是v - u这条边的权值,或苹果数目 
			}
		} 
	}
}

原理:

在树这种数据结构上做DP很常见:

给出一棵树,要求以最少代价(或最大收益)完成给定的操作。在树上做 DP显得非常自然,因为树本身有“子结构”性质(树和子树),具有递归性,符合5.1.2节提到的“记忆化递归”的思路,所以树形 DP一般就这样编程。

基于树的解题步骤一般是先把树转换为有根树(如果是几棵互不连通的树,就加一个虚拟根,它连接所有孤立的树),然后在树上做DFS,递归到最底层的叶子节点,再一层层返回信息更新至根节点。显然,树上DP所操作的就是这一层层返回的信息。不同的题目需要灵活设计不同的DP状态和转移方程。

核心:

树的搭建

// 没有上司的酒会 创建树代码
void addedge(int a, int b) {
	G[from].push_back(to);
	father[to] = from;
}

for (int i = 1; i < n; i++) {
	int u, v; cin >> u >> v;
	addedge(v, u);
}

使用递归(从根开始,先搜索下面的,再操作上面的)

// 没有上司的酒会 部分代码 
void dfs(int u) {
	// 初始化先 
	dp[u][0] = 0;
	dp[u][1] = val[u];
	// 遍历孩子
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		// 一定要先搜索孩子,然后再操作这颗树的, 孩子是基础 
		dfs(v);
		dp[u][i] += dp[v][0]; //父结点选择,子节点不选 
		dp[u][0] += max(dp[v][0], dp[v][1]); // 父结点不选,子节点随意 
	} 
} 

寻找根节点

// 根的father为0 
int t = 1;
while(father[t]) t = father[t];

题目:

1、二叉苹果树  洛谷p2015

2、没有上司的舞会 洛谷p1352

3、有线电视网 洛谷p1273 poj1155

经典题:hdu 1520 2196

树形背包:poj 1947 2486  hdu 1011 1561 4003

洛谷 4322 1352 1040 1122 1273 2014 2585 3047 3698 5658 2607 3477 4395 4516

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值