树形DP——题目

背景知识:链式前向星,DFS,线性DP,背包,树形结构

先刷几天题,我看网上根本就没个入门的树形DP文章,等刷完题看看能不能写个好点的文章

上来就是俩绿题,毕竟树形DP没简单题
没有上司的舞会,树上DP入门

分析:给定树上每个点的权值,并且以u为根节点时,如果选择了u的权值那么u的儿子的权值就不能选取,如果选取了u的儿子节点的权值,那么u的权值就不能选取。

设计dp[i][j]为节点i分别在选与不选情况下的最大值,dp[i][0]代表不选,dp[i][1]代表选

我们很容易的能写出方程
dp[i][0]=max(max(dp[i的儿子][1]+dp[i][0],dp[i][0]),max(dp[i的儿子][1],dp[i的儿子][0]));
dp[i][1]=max(max(dp[i][1],dp[i][1]+dp[i的儿子][0]),dp[i的儿子][0]);

也能看出这里我们需要循环遍历i的所有儿子。

但是我们是要从上往下还是从下往上去实现dp过程呢?
显然,如果从上往下,我们无法处理子节点传给父节点的后效性。

#include <iostream>

using namespace std;
#define N 6000+100
struct node
{
    int to,nex;
};
node edge[N<<1];
int head[N],tot,rd[N];
int dp[N][2];
void add(int from,int to)
{
    edge[++tot].to=to;
    edge[tot].nex=head[from];
    head[from]=tot;
}
void DFS(int x)//重点
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        DFS(edge[i].to);
        dp[x][1]=max(max(dp[x][1],dp[x][1]+dp[edge[i].to][0]),dp[edge[i].to][0]);
        dp[x][0]=max(max(dp[x][0],dp[edge[i].to][1]+dp[x][0]),max(dp[edge[i].to][1],dp[edge[i].to][0]));

    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>dp[i][1];
    }
    for(int i=1,from,to;i<=n-1;i++)
    {
        cin>>from>>to;
        rd[from]++;
        add(to,from);

    }
    int root=0;
    for(int i=1;i<=n;i++)
    {
        if(rd[i]==0)
        {
            root=i;
            break;
        }
    }
    DFS(root);
    cout<<max(dp[root][0],dp[root][1])<<endl;
    return 0;
}

二叉苹果树,书上背包模板
莫说二叉,二十叉也一样

分析:给定了每条边的权值,我们需要保留m条边,使得我们保留的边的总权值最大,很明显,我们有一个容量为m的背包,在树这个货物之间会互相影响的货堆里要求把背包整的权值最大。

那么我们设计dp状态为
dp[i][j]:以i为根节点的子树,保留m条边获得的最大权值。

状态方程有
dp[now][j]=max(dp[now][j],dp[now][j-k-1]+dp[e[i].to][k]+e[i].dis);
其中e[i].to是目前now的儿子节点
e[i].dis是链接now和now的儿子节点的边
翻译过来就是
now节点为根的子树,保留j条边的最大值,就是
1.now节点保留j-k-1条边加上儿子节点保留k条边加上链接now和now的儿子节点的边的权值和
2.now节点保留j条边
1 2取最大值

#include <bits/stdc++.h>
#define maxn 105
using namespace std;

int n,m,dp[maxn][maxn];

struct node
{
	int dis,to,next;
};
node e[maxn<<1];
int tot,head[maxn];
void add(int from,int to,int dis)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
	e[tot].dis=dis;
}
int sz[maxn];
void dfs(int now,int fa)
{
	for(int i=head[now];i;i=e[i].next)
    {
		if(e[i].to==fa)
            continue;
		dfs(e[i].to,now);
		sz[now]+=sz[e[i].to]+1;
		for(int j=min(sz[now],m);j;--j)
        {
            for(int k=min(sz[e[i].to],j-1);k>=0;--k)
            {
                dp[now][j]=max(dp[now][j],dp[now][j-k-1]+dp[e[i].to][k]+e[i].dis);
            }
        }
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1,u,v,w;i<n;++i)
    {
		cin>>u>>v>>w;
		add(u,v,w);
        add(v,u,w);
	}
	dfs(1,0);
	cout<<dp[1][m]<<endl;
	return 0;
}

通过两题总结一下就是
dp状态一般是以u为根节点的子树怎样怎样,或者是u节点怎样怎样
dp过程一般是从下往上
dp每个节点通常和此节点的所有儿子节点有关

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树形动态规划(Tree DP)是一种常用的动态规划算法,用于解决树结构相关的问题。在Python中,可以使用递归或者迭代的方式实现树形DP树形DP的基本思想是,从树的叶子节点开始,逐层向上计算每个节点的状态,并利用已经计算过的节点状态来更新当前节点的状态。这样可以通过自底向上的方式,逐步计算出整个树的最优解。 下面是一个简单的示例,演示如何使用树形DP解决一个二叉树中节点权值之和的最大值问题: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def max_sum(root): if root is None: return 0 # 递归计算左右子树的最大权值和 left_sum = max_sum(root.left) right_sum = max_sum(root.right) # 当前节点的最大权值和为当前节点值加上左右子树中较大的权值和 return root.val + max(left_sum, right_sum) # 构建一个二叉树 root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5) # 计算二叉树中节点权值之和的最大值 result = max_sum(root) print(result) ``` 这段代码中,我们定义了一个`TreeNode`类来表示二叉树的节点,其中`val`表示节点的权值,`left`和`right`分别表示左子节点和右子节点。`max_sum`函数使用递归的方式计算二叉树中节点权值之和的最大值,通过比较左右子树的最大权值和来确定当前节点的最大权值和。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值