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;
}
相关推荐
iAkuya2 分钟前
(leetcode)力扣100 62N皇后问题 (普通回溯(使用set存储),位运算回溯)
算法·leetcode·职场和发展
近津薪荼2 分钟前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
xiaoye-duck4 分钟前
吃透 C++ STL list:从基础使用到特性对比,解锁链表容器高效用法
c++·算法·stl
松☆7 分钟前
CANN与大模型推理:在边缘端高效运行7B参数语言模型的实践指南
人工智能·算法·语言模型
_F_y9 分钟前
C++重点知识总结
java·jvm·c++
java干货18 分钟前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
皮皮哎哟26 分钟前
数据结构:嵌入式常用排序与查找算法精讲
数据结构·算法·排序算法·二分查找·快速排序
程序员清洒34 分钟前
CANN模型剪枝:从敏感度感知到硬件稀疏加速的全链路压缩实战
算法·机器学习·剪枝
vortex51 小时前
几种 dump hash 方式对比分析
算法·哈希算法
初願致夕霞1 小时前
Linux_进程
linux·c++