一、树形DP+分组背包
\(DP\)模型:树形套分组
设\(u\)为根节点,把\(u\)的每一个后继看成是一个个独立的物品组,以体积作为决策去枚举。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int h[N], e[N], ne[N], idx;
int f[N][N]; //f[u][j] 以u为根结点的子树中,总体积不超过j的方案下,最大总价值
int v[N], w[N];
int n, m;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
//遍历以u为根结点的子树
void dfs(int u) {
//1、如果选择了以u为根结点的子树,那么必须保证剩余空间大于等于v[u]
//2、在能装下的情况下,而且u 结点必选,最大价值初始化为w[u]
for (int i = v[u]; i <= m; i++) f[u][i] = w[u];
//遍历每个子结点
for (int i = h[u]; ~i; i = ne[i]) {
dfs(e[i]);//通过递归子结点,准备好子结点的数据,可以依赖于子结点的数据进行判断
for (int j = m; j >= v[u]; j--) //u和子树一共的体积
for (int k = 0; k <= j - v[u]; k++) //分给子树e[i]的体积
f[u][j] = max(f[u][j], f[u][j - k] + f[e[i]][k]);
}
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof(h));
int root;
for (int i = 1; i <= n; i++) {
int p;
cin >> v[i] >> w[i] >> p;
if (p == -1) root = i;
else add(p, i);
}
//树上dp
dfs(root);
printf("%d\n", f[root][m]);//整棵树,在体积为m时的最大价值
return 0;
}
二、多叉树转二叉树实现
三、dfs序
TODO 此方法还没有研究明白,不准备讲
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
/*
需要先学会dfs序,dfs序后倒着做01背包,f[i][j]可以理解为从i往后剩余j的容量产生的最大值
*/
//邻接表
int h[N], ne[N], e[N], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int n, m;
int v1[N], w1[N];
int f[N][N];
//此题我目前dfs序的功力不够,需要学习dfs序后再来进行思考解决
//TODO
int size[2 * N]; //以i为根结点的子树中结点的个数
int dfs_order[2 * N];
int v2[N], w2[N];
int pos;
/**
* 功能:dfs序
* 本质:就是执行一遍树的dfs,找一个辅助数组记录一下dfs序
* @param p 结点编号
*/
void dfs(int p) {
dfs_order[p] = ++pos; //记录结点p在dfs序中是第几个,下标从1开始
//维护一下子树的结点个数
size[pos] = 1; //以dfs序中pos号为根的子树中结点个数
v2[pos] = v1[p]; //将原来树中p号结点的体积转移到pos号dfs序的结点体积上去
w2[pos] = w1[p]; //将原来树中p号结点的价值转移到pos号dfs序的结点价值上去
//遍历邻接表
for (int i = h[p]; i != -1; i = ne[i]) {
int j = e[i];
dfs(j);
//收集所有子结点的个数
size[dfs_order[p]] += size[dfs_order[j]];
}
}
int main() {
cin >> n >> m;
int root;
//初始化邻接表
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++) {
int p;
cin >> v1[i] >> w1[i] >> p;
if (p == -1) root = i; //记录根结点
else add(p, i); //维护邻接表
}
//dfs序
dfs(root);
//倒着做01背包
for (int i = n; i >= 1; i--)
for (int j = 0; j <= m; j++) {
f[i][j] = f[i + size[i]][j];
if (j >= v2[i]) f[i][j] = max(f[i][j], f[i + 1][j - v2[i]] + w2[i]);
}
//输出
cout << f[1][m] << endl;
return 0;
}