当前位置: 首页 > news >正文

树上背包问题动态规划

目录

树状动态规划概述

示例 

求解思路 


树状动态规划概述

树状动态规划(Tree DP)是一种在树结构上进行动态规划的方法。在树状DP中,我们利用树的特殊结构性质,通过递归地向下更新子节点的状态,最终得到整个树的最优解或其他需要的信息。

树状DP通常包含以下步骤:

  1. 定义状态:根据问题的要求,定义每个节点的状态。这可以是一个数值、一个数组、一个结构体等,取决于问题的具体情况。
  2. 设计转移方程:根据问题的要求,确定每个节点的状态如何从其子节点的状态转移而来。这通常通过遍历节点的子节点,并利用子节点的状态来更新当前节点的状态来实现。
  3. 确定初始状态:确定叶节点的初始状态,这是递归的终止条件。
  4. 递归地进行状态转移:从树的顶部开始,递归地向下进行状态转移,直到所有节点的状态都被计算出来。

示例 

问题描述: 给定一棵有根树,每个节点有两个属性:权重和价值。节点的权重表示该节点所需要的空间,节点的价值表示该节点的价值。现在有一个给定的背包容量,要求选择一些节点放入背包中,使得总权重不超过背包容量,同时总价值最大。

输入:

  • 一棵树的节点数
  • 每个节点的权重和价值
  • 背包容量

输出:

  • 最大的总价值

求解思路 

这个例子是一个经典的背包问题在树结构中的应用。我们需要在给定的一棵有根树中,选择一些节点放入背包中,使得总权重不超过背包容量,同时总价值最大。

为了解决这个问题,我们可以使用动态规划的方法。具体思路如下:

  1. 定义状态:我们定义dp[i][j]表示以节点i为根节点的子树中,在背包容量为j的情况下,可以获得的最大总价值。

  2. 状态转移方程:对于节点i的每个孩子节点child,我们需要考虑两种情况:

    • 不选择child节点:此时dp[i][j]不变。
    • 选择child节点:此时需要从剩余容量j减去child节点的权重,即j - tree[child].weight,并从子问题dp[child][j - tree[child].weight]中得到最大价值,再加上child节点的价值tree[child].value。整体来看,选择child节点后的最大总价值为dp[child][j - tree[child].weight] + tree[child].value。

    综合考虑上述两种情况,我们可以得到状态转移方程:

    dp[i][j] = max(dp[i][j], dp[child][j - tree[child].weight] + tree[child].value)

    其中,child为节点i的孩子节点。

  3. 初始化:我们将dp数组初始化为0,表示初始时没有选择任何节点。

  4. 从根节点开始进行深度优先搜索(DFS),按照上述状态转移方程更新dp数组中的值。最终,dp[1][背包容量]即为所求的最大总价值。

下面是代码中主要部分的解释:

 
void dfs(int node) {for (int child : tree[node].children) {// 对每个孩子节点进行深度优先搜索dfs(child);// 更新dp数组for (int i = dp[node].size() - 1; i >= tree[node].weight; i--) {dp[node][i] = max(dp[node][i], dp[child][i - tree[node].weight] + tree[child].value);}}
}

在dfs函数中,我们首先对当前节点的每个孩子节点进行深度优先搜索。然后,通过一个循环,从dp[node]的最后一个元素开始向前更新dp[node]的值。这里使用了倒序循环的方式,是因为我们需要保证在更新dp[node][i]时,dp[child][i - tree[node].weight]已经被计算过(即在dp[node]的前面位置)。同时,我们需要确保总权重不超过背包容量,所以我们从tree[node].weight开始遍历。

最后,在主函数中,我们输入节点数和每个节点的权重、价值信息,构建树结构,并调用dfs函数进行求解。最终结果存储在dp[1][背包容量]中。

希望以上详细解释能够帮助你理解这个树状动态规划问题的解决方法。如有任何疑问,请随时提出。

示例:

输入:
节点数 = 5
节点 1: 权重 = 2, 价值 = 3
节点 2: 权重 = 1, 价值 = 2
节点 3: 权重 = 3, 价值 = 4
节点 4: 权重 = 2, 价值 = 2
节点 5: 权重 = 1, 价值 = 1
背包容量 = 5输出:
最大总价值 = 9

C++代码实现:

#include <iostream>
#include <vector>
using namespace std;struct Node {int weight;int value;vector<int> children;
};vector<Node> tree;  // 存储树节点的信息
vector<vector<int>> dp;  // 存储动态规划的结果void dfs(int node) {for (int child : tree[node].children) {dfs(child);for (int i = dp[node].size() - 1; i >= tree[node].weight; i--) {dp[node][i] = max(dp[node][i], dp[child][i - tree[node].weight] + tree[child].value);}}
}int main() {int n;  // 节点数cin >> n;tree.resize(n + 1);  // 从编号1开始存储节点信息dp.resize(n + 1, vector<int>(n + 1, 0));  // 初始化动规数组for (int i = 1; i <= n; i++) {cin >> tree[i].weight >> tree[i].value;}// 构建树结构for (int i = 2; i <= n; i++) {int parent;cin >> parent;tree[parent].children.push_back(i);}dfs(1);  // 从根节点开始进行深度优先搜索cout << "最大总价值 = " << dp[1][n] << endl;return 0;
}

这段代码首先通过输入构建了一棵树,并使用动态规划方法计算了最大总价值。其中,dfs函数进行了深度优先搜索和动态规划的计算,dp数组用于存储动态规划的结果。

http://www.lryc.cn/news/168879.html

相关文章:

  • linux查看进程对应的线程(数)
  • Python中的桌面应用开发库有哪些?
  • 【大数据】Neo4j 图数据库使用详解
  • Windows11系统C盘用户文件夹下用户文件夹为中文,解决方案
  • Python正则表达式(re)
  • 【PyTorch 08】如果要手动安装对应的包
  • 黑马JVM总结(十二)
  • 彻底搞懂线程池原理以及创建方式
  • FreeSWITCH 1.10.10 简单图形化界面9 - 鼎兴FXO网关SIP中继内网IPPBX落地
  • Oracle数据如何迁移导入到MySQL
  • 卡尔曼滤波(Kalman Filter)原理浅析-数学理论推导-1
  • Linux 文件创建、查看
  • WPF 如何让xmal的属性换行显示 格式化
  • Linux学习之MySQL主从复制
  • 【JavaSE笔记】抽象类与接口
  • 详谈操作系统中的内核态和用户态
  • OpenWrt KernelPackage分析
  • 第 363 场 LeetCode 周赛题解
  • ffplay源码解析-main入口函数
  • 这些Coding套路你不会还不知道吧?
  • Spring Boot深度解析:快速开发的秘密
  • mysql数据库备份(mysqldump)
  • linux Nginx+Tomcat负载均衡、动静分离
  • ts 枚举类型原理及其应用详解
  • 腾讯mini项目-【指标监控服务重构】2023-08-23
  • C- ssize_t size_t
  • ubuntu20.04 Supervisor 开机自启动脚本一文配置
  • 【面试刷题】——函数指针和指针函数
  • 目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)
  • 【100天精通Python】Day61:Python 数据分析_Pandas可视化功能:绘制饼图,箱线图,散点图,散点图矩阵,热力图,面积图等(示例+代码)