C++树形DP(树上分组背包)

树形DP 代码框架见下 对应小明的背包6 蓝桥云课

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

#define type int
#define maxn 1010
#define maxv 110

struct Item {
    int vol;
    int wei;
}items[maxn];

//child[i][j]代表i个第j个儿子是child[i][j]
vector<int> child[maxn];
//dp[i][j]代表以i为根的子树中,选择容量总和为j的物品,得到的最优价值
type dp[maxn][maxv];
//sumVol[i] 代表以i为根的子树中,所有子节点的总容量
int sumVol[maxn];
//n件物品,最大容量为V
int n, V;
// 状态初始值
type KnapsackTree_InitValue(int u) {
    return 0;
}
//非法状态值
type KnapsackTree_InfValue() {
    return -1000000000;
}
//状态转移的最优函数,有可能是最大值,有可能是最小值,有可能是方案数
type KnapsackTree_Opt(type curVal, type preVal, type itemVal) {
    return max(curVal, preVal + itemVal);
}
//这一步非常关键,目的是把滚动数组dpu中计算的数据,转移到dp数组中
//dpu是临时数据,递归结束就销毁了
//dp是持久化数据(全局数据),递归结束一直保存
void KnapsackTree_Post(int u, type dpu[maxv]) {
    for (int j = 1; j <= V; ++j) {
        // 当前这个物品不选
        dp[u][j] = (j == 0) ? KnapsackTree_InitValue(u) : KnapsackTree_InfValue();
        // 当前物品选择的情况
        if (j >= items[u].vol) {
            dp[u][j] = KnapsackTree_Opt(
                dp[u][j],
                dpu[j - items[u].vol], items[u].wei
            );
        }
    }
}
// 树上分组初始化
//模版代码 基本不用改
int KnapsackTree_Init(type dpu[2][maxv], int u) {
    int pre = 0;
    dpu[pre][0] = KnapsackTree_InitValue(u);
    for (int i = 1; i <= V; ++i) {
        dpu[pre][i] = KnapsackTree_InfValue();
    }
    return pre;
}

type KnapsackTree_GetAnswer(int root) {
    type ans = KnapsackTree_InfValue();
    for (int i = 0; i <= V; ++i) {
        ans = KnapsackTree_Opt(ans, dp[root][i], 0);
    }
    return ans;
}
//模版代码,基本不用改
void KnapsackTree_DFS(int u, int fat) {
    type dpu[2][maxv];
    int pre = KnapsackTree_Init(dpu, u);
    int cur = 1 - pre;
    sumVol[u] = items[u].vol;
    for (int i = 0; i < child[u].size(); ++i) {
        int v = child[u][i];
        if (v == fat) continue;
        KnapsackTree_DFS(v, u);
        sumVol[u] += sumVol[v];
        for (int j = 0; j <= V; ++j) {
            dpu[cur][j] = KnapsackTree_InfValue();
            for (int k = 0; k <= j && k <= sumVol[v]; ++k) {
                type tmp = dpu[pre][j - k] + dp[v][k];
                dpu[cur][j] = KnapsackTree_Opt(dpu[cur][j], dpu[pre][j - k], dp[v][k]);
            }
        }
        swap(pre, cur);
    }
    KnapsackTree_Post(u, dpu[pre]);
}
int main()
{
    cin >> n >> V;
    for (int i = 1; i <= n; ++i) {
        int x;
        cin >> items[i].vol >> items[i].wei >> x;
        child[x].push_back(i);
    }
    KnapsackTree_DFS(0, -1);
    cout << KnapsackTree_GetAnswer(0) << endl;

    // 请在此输入您的代码
    return 0;
}

代码 1 对应蓝桥云课 包含1的树上联通计数块2

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

#define type long long 
#define maxn 2010
#define maxv 2010
#define mod 998244353

struct Item {
    int vol;
    int wei;
}items[maxn];

//child[i][j]代表i个第j个儿子是child[i][j]
vector<int> child[maxn];
//dp[i][j]代表以i为根的子树中,选择容量总和为j的物品,得到的最优价值
type dp[maxn][maxv];
//sumVol[i] 代表以i为根的子树中,所有子节点的总容量
int sumVol[maxn];
//n件物品,最大容量为V
int n, V;
// 状态初始值
type KnapsackTree_InitValue(int u) {
    return 1;
}
//非法状态值
type KnapsackTree_InfValue() {
    return 0;
}
//状态转移的最优函数,有可能是最大值,有可能是最小值,有可能是方案数
type KnapsackTree_Opt(type curVal, type preVal, type itemWei) {
    return (curVal + preVal * itemWei) % mod;
}
//这一步非常关键,目的是把滚动数组dpu中计算的数据,转移到dp数组中
//dpu是临时数据,递归结束就销毁了
//dp是持久化数据(全局数据),递归结束一直保存
void KnapsackTree_Post(int u, type dpu[maxv]) {
    for (int j = 0; j <= V; ++j) {
        // 当前这个物品不选
        dp[u][j] = (j == 0) ? KnapsackTree_InitValue(u) : KnapsackTree_InfValue();
        // 当前物品选择的情况
        if (j >= items[u].vol) {
            dp[u][j] = KnapsackTree_Opt(
                dp[u][j],
                dpu[j - items[u].vol], items[u].wei
            );
        }
    }
}
// 树上分组初始化
//模版代码 基本不用改
int KnapsackTree_Init(type dpu[2][maxv], int u) {
    int pre = 0;
    dpu[pre][0] = KnapsackTree_InitValue(u);
    for (int i = 1; i <= V; ++i) {
        dpu[pre][i] = KnapsackTree_InfValue();
    }
    return pre;
}
// 根据题目意思做修改
type KnapsackTree_GetAnswer(int root) {

    return dp[root][V];
}
//模版代码,基本不用改
void KnapsackTree_DFS(int u, int fat) {
    type dpu[2][maxv];
    int pre = KnapsackTree_Init(dpu, u);
    int cur = 1 - pre;
    sumVol[u] = items[u].vol;
    for (int i = 0; i < child[u].size(); ++i) {
        int v = child[u][i];
        if (v == fat) continue;
        KnapsackTree_DFS(v, u);
        sumVol[u] += sumVol[v];
        for (int j = 0; j <= V; ++j) {
            dpu[cur][j] = KnapsackTree_InfValue();
            for (int k = 0; k <= j && k <= sumVol[v]; ++k) {
                dpu[cur][j] = KnapsackTree_Opt(dpu[cur][j], dpu[pre][j - k], dp[v][k]);
            }
        }
        swap(pre, cur);
    }
    KnapsackTree_Post(u, dpu[pre]);
}
int main()
{
    cin >> n >> V;
    for (int i = 1; i <= n; ++i) {
        items[i].vol = 1;
        items[i].wei = 1;
    }
    for (int i = 0; i < n - 1; ++i) {
        int x, y;
        cin >> x >> y;
        child[x].push_back(y);
        child[y].push_back(x);
    }
    KnapsackTree_DFS(1, -1);
    cout << KnapsackTree_GetAnswer(1) << endl;

    // 请在此输入您的代码
    return 0;
}

代码练习 2 最小子树 代码见下,对应蓝桥云课

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

#define type int
#define maxn 100010
#define maxv 3

struct Item {
    int vol;
    int wei;
}items[maxn];

//child[i][j]代表i个第j个儿子是child[i][j]
vector<int> child[maxn];
//dp[i][j]代表以i为根的子树中,选择容量总和为j的物品,得到的最优价值
type dp[maxn][maxv];
//sumVol[i] 代表以i为根的子树中,所有子节点的总容量
int sumVol[maxn];
//n件物品,最大容量为V
int n, V;
// 状态初始值
type KnapsackTree_InitValue(int u) {
    return items[u].wei;
}
//非法状态值
type KnapsackTree_InfValue() {
    return 1000000000;
}
//
//如果是加边,直接返回 KnapsackTree_InfValue
//如果是删边,需要根据v值来判定
type KnapsackTree_CurInitValue(type dpu[maxv], int j){
  return (j > 0) ? dpu[j-1] : KnapsackTree_InfValue();
}
//状态转移的最优函数,有可能是最大值,有可能是最小值,有可能是方案数
type KnapsackTree_Opt(type curVal, type preVal, type itemVal) {
    return min(curVal, preVal + itemVal);
}
//这一步非常关键,目的是把滚动数组dpu中计算的数据,转移到dp数组中
//dpu是临时数据,递归结束就销毁了
//dp是持久化数据(全局数据),递归结束一直保存
void KnapsackTree_Post(int u, type dpu[maxv]) {
  for(int j=0; j <= V; ++j){
    dp[u][j] = dpu[j];
  }
}
// 树上分组初始化
//模版代码 基本不用改
int KnapsackTree_Init(type dpu[2][maxv], int u) {
    int pre = 0;
    dpu[pre][0] = KnapsackTree_InitValue(u);
    for (int i = 1; i <= V; ++i) {
        dpu[pre][i] = KnapsackTree_InfValue();
    }
    return pre;
}

type KnapsackTree_GetAnswer(int root) {
    type ans = KnapsackTree_InfValue();
    for(int i=1; i <= n; ++i){
      int mv = (i == root) ? V:V-1;
      for(int j=0; j <= mv; ++j){
        ans = KnapsackTree_Opt(ans, dp[i][j], 0);
      }
    }
    return ans;
}
//模版代码,基本不用改
void KnapsackTree_DFS(int u, int fat) {
    type dpu[2][maxv];
    int pre = KnapsackTree_Init(dpu, u);
    int cur = 1 - pre;
    sumVol[u] = items[u].vol;
    for (int i = 0; i < child[u].size(); ++i) {
        int v = child[u][i];
        if (v == fat) continue;
        KnapsackTree_DFS(v, u);
        sumVol[u] += sumVol[v];
        for (int j = 0; j <= V; ++j) {
            dpu[cur][j] = KnapsackTree_CurInitValue(dpu[pre], j);
            for (int k = 0; k <= j && k <= sumVol[v]; ++k) {
                dpu[cur][j] = KnapsackTree_Opt(dpu[cur][j], dpu[pre][j - k], dp[v][k]);
            }
        }
        swap(pre, cur);
    }
    KnapsackTree_Post(u, dpu[pre]);
}
int main()
{
    cin >> n;
    V = 2;
    for (int i = 1; i <= n; ++i) {
        items[i].vol = 1;
        cin >> items[i].wei;
    }
    for(int i=0; i < n-1; ++i){
      int x, y;
      cin >> x >> y;
      child[x].push_back(y);
      child[y].push_back(x);
    }
    KnapsackTree_DFS(1, -1);
    cout << KnapsackTree_GetAnswer(1) << endl;

    // 请在此输入您的代码
    return 0;
}

代码练习 3 对应蓝桥云课 取气球 代码见下

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

#define type int
#define maxn 5010
#define maxv 5010

struct Item {
    int vol;
    int wei;
    int edge;
}items[maxn];

//child[i][j]代表i个第j个儿子是child[i][j]
vector<int> child[maxn];
//dp[i][j]代表以i为根的子树中,选择容量总和为j的物品,得到的最优价值
type dp[maxn][maxv];
//sumVol[i] 代表以i为根的子树中,所有子节点的总容量
int sumVol[maxn];
//n件物品,最大容量为V
int n, V;
// 状态初始值
type KnapsackTree_InitValue(int u) {
    return 0;
}
//非法状态值
type KnapsackTree_InfValue() {
    return 1000000000;
}
//
//如果是加边,直接返回 KnapsackTree_InfValue
//如果是删边,需要根据v值来判定
type KnapsackTree_CurInitValue(type dpu[maxv], int j) {
    return KnapsackTree_InfValue();
}
//状态转移的最优函数,有可能是最大值,有可能是最小值,有可能是方案数
type KnapsackTree_Opt(type curVal, type preVal, type itemVal) {
    return min(curVal, preVal + itemVal);
}
//这一步非常关键,目的是把滚动数组dpu中计算的数据,转移到dp数组中
//dpu是临时数据,递归结束就销毁了
//dp是持久化数据(全局数据),递归结束一直保存
void KnapsackTree_Post(int u, type dpu[maxv]) {
    for (int j = 0; j <= V; ++j) {
        dp[u][j] = dpu[j];
    }
    // 割掉绳子
    dp[u][sumVol[u]] = KnapsackTree_Opt(dp[u][sumVol[u]], items[u].edge, 0);
    // 戳破气球
    dp[u][sumVol[u] - 1] = KnapsackTree_Opt(dp[u][sumVol[u] - 1], items[u].wei, 0);
}
// 树上分组初始化
//模版代码 基本不用改
int KnapsackTree_Init(type dpu[2][maxv], int u) {
    int pre = 0;
    dpu[pre][0] = KnapsackTree_InitValue(u);
    for (int i = 1; i <= V; ++i) {
        dpu[pre][i] = KnapsackTree_InfValue();
    }
    return pre;
}

type KnapsackTree_GetAnswer(int root, int w) {
    for (int i = n; i >= 0; --i) {
        if (dp[root][i] <= w) {
            return i;
        }
    }
    return 0;
}
//模版代码,基本不用改
void KnapsackTree_DFS(int u, int fat) {
    type dpu[2][maxv];
    int pre = KnapsackTree_Init(dpu, u);
    int cur = 1 - pre;
    sumVol[u] = items[u].vol;
    for (int i = 0; i < child[u].size(); ++i) {
        int v = child[u][i];
        if (v == fat) continue;
        KnapsackTree_DFS(v, u);
        sumVol[u] += sumVol[v];
        for (int j = 0; j <= V; ++j) {
            dpu[cur][j] = KnapsackTree_CurInitValue(dpu[pre], j);
            for (int k = 0; k <= j && k <= sumVol[v]; ++k) {
                dpu[cur][j] = KnapsackTree_Opt(dpu[cur][j], dpu[pre][j - k], dp[v][k]);
            }
        }
        swap(pre, cur);
    }
    KnapsackTree_Post(u, dpu[pre]);
}
int main()
{
    int w;
    cin >> n >> w;
    V = n;
    for (int i = 1; i <= n; ++i) {
        items[i].vol = 1;
        cin >> items[i].wei;
    }
    items[1].edge = KnapsackTree_InfValue();
    for (int i = 1; i <= n - 1; ++i) {
        int x, y;
        cin >> x >> y;
        child[x].push_back(i + 1);
        items[i + 1].edge = y;
    }
    KnapsackTree_DFS(1, -1);
    cout << KnapsackTree_GetAnswer(1, w) << endl;

    // 请在此输入您的代码
    return 0;
}
相关推荐
历程里程碑3 小时前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
A尘埃3 小时前
物流公司配送路径动态优化(Q-Learning算法)
算法
郝学胜-神的一滴3 小时前
深入浅出:使用Linux系统函数构建高性能TCP服务器
linux·服务器·开发语言·网络·c++·tcp/ip·程序人生
天若有情6733 小时前
【自研实战】轻量级ASCII字符串加密算法:从设计到落地(防查岗神器版)
网络·c++·算法·安全·数据安全·加密
啊森要自信4 小时前
CANN ops-cv:AI 硬件端视觉算法推理训练的算子性能调优与实战应用详解
人工智能·算法·cann
czy87874754 小时前
深入了解 C++ 中的 `std::bind` 函数
开发语言·c++
我在人间贩卖青春4 小时前
C++之继承的方式
c++·private·public·protected·继承方式
仟濹4 小时前
算法打卡day2 (2026-02-07 周五) | 算法: DFS | 3_卡码网99_计数孤岛_DFS
算法·深度优先
驭渊的小故事4 小时前
简单模板笔记
数据结构·笔记·算法
YuTaoShao4 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法一)前后缀分解
算法·leetcode·职场和发展