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

树上背包(P2014 [CTSC1997] 选课)

树上的背包问题,简单来说就是背包问题与树形 DP 的结合。

树上背包问题概述

树上背包问题指的是在树形结构上进行动态规划,结合背包问题的思想,通常用于解决子树资源分配或依赖选择的问题。这类问题通常涉及节点间的依赖关系,需要在树上进行状态转移。

基本思路

  1. 状态定义:通常定义 dp[u][j] 表示以节点 u 为根的子树中,选择 j 个节点(或消耗 j 单位资源)时的最优解(如最大价值或最小代价)。
  2. 转移方程:通过遍历子节点,将子节点的状态合并到父节点中。对于每个子节点 v,枚举父节点和子节点的分配情况,更新状态。

代码框架(C++)

以下是树上背包问题的通用代码框架:

#include <vector>
#include <algorithm>
using namespace std;const int N = 1e3 + 5; // 节点数
const int M = 1e3 + 5; // 背包容量
vector<int> tree[N];
int dp[N][M]; // dp[u][j]: 以u为根的子树,容量为j时的最优解
int n, m; // 节点数和背包容量void dfs(int u, int parent) {// 初始化,通常将当前节点u的状态初始化为某种值for (int j = 0; j <= m; ++j) {dp[u][j] = ...; // 根据问题初始化}for (int v : tree[u]) {if (v == parent) continue;dfs(v, u); // 递归处理子节点// 合并子节点状态到父节点for (int j = m; j >= 0; --j) { // 注意逆序枚举,避免重复计算for (int k = 0; k <= j; ++k) {dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);}}}
}

关键点

  1. 递归处理子树:通过DFS遍历树,确保子节点的状态先于父节点计算。
  2. 逆序枚举背包容量:避免重复计算,类似于01背包的优化方式。
  3. 状态合并:将子节点的状态通过枚举分配方式合并到父节点中。

示例问题:子树节点选择

假设每个节点有一个价值 val[u] 和体积 w[u],要求在子树中选择节点,使得总体积不超过 m,且总价值最大。

int val[N], w[N]; // 节点的价值和体积void dfs(int u, int parent) {// 初始化:选择当前节点ufor (int j = w[u]; j <= m; ++j) {dp[u][j] = val[u];}for (int v : tree[u]) {if (v == parent) continue;dfs(v, u);// 合并子节点状态for (int j = m; j >= w[u]; --j) {for (int k = 0; k <= j - w[u]; ++k) {dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);}}}
}

复杂度分析

  • 时间复杂度:O(n * m^2),其中 n 是节点数,m 是背包容量。通过优化可以降为 O(n * m)
  • 空间复杂度:O(n * m)

优化技巧

  1. 子树大小优化:限制背包容量的枚举范围为子树大小,减少无效计算。

    int size[N]; // 记录子树大小void dfs(int u, int parent) {size[u] = 1;for (int j = 0; j <= m; ++j) dp[u][j] = ...;for (int v : tree[u]) {if (v == parent) continue;dfs(v, u);size[u] += size[v];for (int j = min(m, size[u]); j >= 0; --j) {for (int k = 0; k <= min(j, size[v]); ++k) {dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);}}}
    }
    

  2. 滚动数组:如果空间紧张,可以用滚动数组优化空间。

常见应用场景

  1. 树上依赖背包问题(如选择子树中的节点需满足依赖关系)。
  2. 树形结构上的资源分配问题(如带宽分配、任务调度)。
  3. 子树统计问题(如统计满足条件的子树数量)。

P2014 [CTSC1997] 选课

思路:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int n, m;
struct {int i, next;//i这次可以不用
}a[302];
int h[302];
int head[302];
int dp[302][302];
void dfs(int i) {while (h[i] != -1) {dfs(a[h[i]].i);for (int j = m; j >= 0; j--) {for (int z = j; z >= 0; z--) {dp[i][j] = max(dp[i][j], dp[h[i]][z] + dp[i][j - z]);}}h[i] = a[h[i]].next;}for (int j = m; j >= 1; j--) {dp[i][j] = dp[i][j - 1] + head[i];}
}
int main(){ios::sync_with_stdio(false);        // 禁用同步cin.tie(nullptr);                   // 解除cin与cout绑定cin >> n >> m;memset(h, -1, sizeof(h));int l;for (int i = 1; i <= n; i++) {cin >> l >> head[i];a[i].i = i;a[i].next = h[l];h[l] = i;}m = m + 1;//0当成一门课且必选dfs(0);cout << dp[0][m] << endl;return 0;
}

http://www.dtcms.com/a/340609.html

相关文章:

  • 经营帮租赁经营板块:解锁资产运营新生态,赋能企业增长新引擎
  • 【最后203篇系列】034 使用SQLite构建简单的任务管理
  • Qt5.9.9 + Windows API 开发系统监控工具 - 教学级项目实战
  • Obsidian 1.9.10升级
  • 19.web api 10
  • SQL-leetcode— 2356. 每位教师所教授的科目种类的数量
  • 有关SWD 仿真和PA.15, PB3, PB4的冲突问题
  • 深入Linux内核:架构设计与核心功能解析
  • CSS3DRenderer+ CSS3DObject实现在 Three.js 中添加文本内容
  • 算法230. 二叉搜索树中第 K 小的元素
  • 10M25DCF484C8G Altera FPGA MAX10
  • 云原生俱乐部-RH294知识点归纳(1)
  • RK-Android11-PackageInstaller安装器自动安装功能实现
  • iOS App 混淆工具实战 医疗健康类 App 的安全与合规保护
  • 电脑驱动免费更新? 这款驱动管理工具:一键扫更新,还能备份恢复,小白也会用~
  • 【知识杂记】方差、标准差、均方误差、均方根误差与平均绝对误差,概念、计算公式、物理意义
  • 微型导轨的快速调平技术如何提升激光加工效率?
  • Python默认参数
  • CPTS--Administrator
  • 【clion】调试脚本并cmake构建Fargo项目win32版本
  • Spring Boot 配置
  • C++---向下取整(>>)与向零取整(/)
  • Vue2封装Axios
  • PyTorch - Developer Notes
  • 《录井工程与管理》——第二章井位勘测技术
  • 精品方案 | GCKontrol与OMNeT++联合仿真在机载网络性能分析中的应用
  • 基于单片机环境火灾安全检测
  • 驾驭复杂表单:用 RxJava 实现响应式表单处理
  • mysql-8.0.37-linux-glibc2.12-x86_64安装
  • 数据结构与算法系列(大白话模式)小学生起点(一)