HihoCoder第十二周——树上DP

这篇博客讨论了如何在树形结构中使用动态规划(DP)策略,以求得最大权值和。作者首先介绍了问题背景,给定一棵树,每个节点带有权值,目标是选择一部分节点进行涂漆,使得涂漆节点的权值和最大。接着,分析了DP的状态转移方程,定义了f(t, m)表示以节点t为根的子树中,选取包含t的m个连通节点的最大权值。最后,提供了具体的代码实现来解决这个问题。" 84915050,7411278,Visual Paradigm IT项目管理框架详解,"['项目管理', '框架', 'IT项目管理', 'Visual Paradigm']

刷油漆

题目

给定一棵树,每个节点有一个权值,将包含1号结点的一部分连通的结点进行涂漆(这里的连通指的是这一些涂漆的结点可以互相到达并且不会经过没有涂漆的结点),使权值和最大。

分析

f(t, m)表示,在以t为根的一棵树中,选出包含根节点t的m个连通的结点,能够获得的最高的评分,然后我们的答案就是f(1, M)。
针对于每一个t,同时求解它的f(t, 0..M),这样的话,我就可以把m视作背包容量,把每个子结点t_child都视作一件单位重量为1的物品,但是和背包问题不同的是,这件物品的总价值并不是单位价值乘以总重量,而是重量为m_child的该物品的价值为f(t_child, m_child)
树上DP

代码

#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
using namespace std;

int n, m, a[101], p[101][101], s, t, used[101];
vector<int> v[101];

void dfs(int k)
{
    for(int i=0; i<v[k].size(); ++i)
    {
        if(used[v[k][i]])
            continue;
        used[v[k][i]] = 1;
        dfs(v[k][i]);
        for(int j=m; j>0; --j)
        {
            for(int x=j; x>0; --x)
                p[k][j] = max(p[k][j],p[k][j-x]+p[v[k][i]][x]);
        }
    }
    for(int i=m; i>0; --i)
        p[k][i] = p[k][i-1] + a[k-1];
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i=0; i<n; ++i)
        scanf("%d", a+i);
    for(int i=0; i<n-1; ++i)
    {
        scanf("%d%d", &s, &t);
        v[s].push_back(t);
        v[t].push_back(s);
    }
    used[1] = 1;
    dfs(1);
    cout<<p[1][m]<<endl;
}
### Python 中基于树构的动态规划实现 #### 背景介绍 动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法[^3]。当涉及到树形,通常会采用一种称为 **树形 DP** 的方法来解决问题。这种方法的核心在于利用树节点之间的父子关系以及递推方程,在遍历过程中逐步构建解决方案。 以下是关于如何在 Python 中实现基于树构的动态规划的具体说明: --- #### 树形 DP 基本原理 1. **定义状态**: 对于每一个节点 `i`,我们可能需要维护一些与其相关的属性值(例如最大路径和、最小覆盖范围等)。这些值可以通过其子节点的状态转移得到。 2. **状态转移方程**: 利用父节点与子节点的关系建立递推公式。常见的场景包括: - 计算从根到叶子的最大/最短路径; - 找出满足某些约束条件的最佳子树配置; 3. **初始化与边界处理**: 需要特别注意叶节点的情况,因为它们没有子节点可以参与状态转移过程。 4. **后序遍历优先**: 由于树形 DP 往往依赖子节点果更新父节点的信息,因此推荐使用深度优先搜索 (DFS) 并按照后序顺序访问各个节点。 --- #### 示例代码:二叉树中的最长递增路径 下面是一个经典的例子——给定一棵二叉树,找到其中任意两个结点之间最长的递增路径长度。 ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def longestIncreasingPath(root: TreeNode) -> int: max_len = 0 def dfs(node): nonlocal max_len if not node: return 0 # 初始化当前节点向左、右延伸的最大递增路径长度 left_inc = 0 right_inc = 0 # 左子树探索 if node.left and node.left.val > node.val: left_inc = dfs(node.left) # 右子树探索 if node.right and node.right.val > node.val: right_inc = dfs(node.right) # 更新全局最大值 current_max_path = 1 + max(left_inc, right_inc) max_len = max(max_len, current_max_path) return current_max_path dfs(root) return max_len ``` 上述代码实现了对二叉树中每条递增路径的查找,并记录下最大的一条路径长度。这里采用了 DFS 来逐层深入并返回各分支上的最佳果[^1]。 --- #### 合回溯法的应用扩展 尽管动态规划擅长解决计数类或优化目标的问题,但在实际应用中有也需要借助回溯技术枚举所有可能性后再筛选符合条件者[^2]。两者合能够更灵活应对不同类型的挑战。 例如对于寻找特定模式匹配的任务来说,先运用回溯穷尽潜在选项集合,再辅以记忆化存储减少冗余运算,则可显著提升效率。 --- #### 总 综上所述,针对树型数据构下的动态规划实践主要围绕以下几个方面展开讨论:一是确立清晰的状态表示形式及其相互间联系规律;二是合理安排迭代次序确保逻辑连贯性;三是综合考虑间空间开销选取合适策略完成最终解答。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值