树形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;
}