7.4 树形DP

7.4 树形DP

树形DP在紫书上9.4那章节提到过一部分,但介绍的非常简单(也可能是当时水平有限),现在在黑书上进行更专项的学习。

树形DP是指在“树”这种数据结构上进行的DP:给出一棵树,要求以某种最优的方式(最少的代价或者最大收益)完成给定的操作。由于树的结构本身具有一定递归的性质(树和子树的关系),比起线性DP,树形DP的状态转移方程更加直观。


树的最大独立集(类题)

我们在紫书上是学习过树的最大独立集问题的,那为什么这里要加个类题呢?

首先这是原问题:

树的最大独立集

对于一棵n个结点的无根树,选出尽量多的结点,使得任何两个结点均不相邻(称为最大独立集),然后输入n-1条无向边,输出一个最大独立集。

那这个类题是什么样呢?

hdu 1520 “Anniversary Party”

一颗有根树上的每个结点有一个权值,相邻的父节点和子节点只能选择一个,问如何选择能够使得权值之和最大。

说是类题,其实几乎没有什么区别。对于无根树,我们可以任意选取一个根节点r将无根树转化为有根树(事实上类题也没有明确说明哪个结点为根节点)。原题的解法如下:

任选一个根r,将无根树转化为有根树,d(i)表示以i为根结点的子树的最大独立集大小。对于“自由”的结点i只有两种决策:如果选了i,那么它的儿子全部不能选,问题转化为求i的所有孙子的d值之和,如果不选,问题转化为求i的所有儿子的d值之和。状态转移方程如下:

d(i)=max{1+Σd(j)|j∈gs(i),Σd(j)|j∈s(i)}

其中,gs(i)和s(i)分别表示i的孙子集合与儿子集合。

类题和原题的关系,就像物品无线的背包问题和硬币问题的关系,只是添加了一个权值的概念,或者可以理解为原题中的每个结点的权值都为1,按照这么理解就可以得到类似的解决方法:

用dp[i][0]表示不选择当前结点i时的最优解,dp[i][1]表示选择当前结点i时的最优解,那么状态转移方程如下:

1.不选择当前结点,那么它的子结点可选可不选,取其中的最大值:

dp[u][0]+=max(dp[son][1],dp[son][0]),u为当前结点,son为u的子节点。

2.选择当前结点,那么它的子节点一定不能选:

dp[u][1]+=dp[son][0]。

代码如下:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=6005;
int value[N],dp[N][N],father[N],n;//value[i]记录第i个结点的权值,father[i]记录第i个结点父节点的编号,没有则为-1 
vector<int>tree[N];//tree[i]记录编号为i的结点的子节点的编号(用son表示更好)
void dfs(int u){
   dp[u][0]=0; dp[u][1]=value[u];//参加宴会至少有本人的权值 
	for (int i=0;i<tree[u].size();i++){
   dfs(tree[u][i]);//递归深搜子节点 
		dp[u][0]+=max(dp[tree[u][i]][1],dp[tree[u][i]][0]);//父节点不选,子节点可选可不选 
		dp[u][1]+=dp[tree[u][i]][0];//父节点选择,子节点不可以选 
	} 
} 
int main(){
   
	while (~scanf("%d",&n)){
   
		for (int i=1;i<=n;i++){
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值