题意
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父>节点。
每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1…N。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
1≤N,V≤100
1≤vi,wi≤100
父节点编号范围:
内部结点:1≤pi≤N;
根节点 pi=−1;
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例
11
题意解析:
每个物品输入次序就是下标,从1-n
假设只有一个主件 和多个n其他附件 那么可以有2^n(排列组合)个主件和附件的集合,也就是解在这些策略中
枚举2^n个可能明显不现实, 我们可以考虑" 一个简单的优化 " 也就是01背包,在主件已经选择的前提下用01背包选择其他附件(每个附件选或者不选)的最优解
因为01背包 "体积相同,但价值高"筛选掉无用策略,所以正解就是dp[j]中的一种 代码中用fn[key][j]
联系题意(树):每个个主件的附件可能是另一个物件的主件(题意中只要已经选了一个物件(不是根) 的主件就可以选这个物件),所以每个附件的本身也是一个集合,通过优先求出附件自己的集合(附件为集合)通过你给附件分配的不同体积,这个附件就有不同的收益
因此再用01背包的选择附件时,加个内部循环 再枚举给附件(附件自己也是一棵树)分配不同体积对应的收益/就可以得到不同体积下的背包收益的最优策略。
那么如何求其附件自己对应不同的分得的到的体积的最优策略呢?
直接用树的递归性质, 每次求一个集合的不同体积的最优策略 就先求出其附件对于分配到不同体积的最优策略即可。
代码能跑18ms以内
#include <iostream>
using namespace std;
const int N =200;
int fn[N][N];
int p[N],v[N],w[N];
int n,V;
int fun(int key,int VV){//key主件下标 VV可分配的体积
//递归的求01
if(VV<v[key])//没有体积选择这个物件
return 0;
for(int i = 1;i <= n;i++)//对附件进行递归
if(p[i] == key)//如果是它的附件
fun(i,VV-v[key]);
fn[key][v[key]] = w[key];//先选择主件, 选了主件才能选择对应的附件
for(int i = 0;i <= n;i++){//01背包 对不同体积求 最大价值
if(p[i] == key)//如果是主件的附件
for(int j = VV;j>=v[key];j--){//从体积最大开始 确保每个附件其对应的集合只能选择一个次
for(int j1 = 0;j1 <=j-v[key];j1++ )//可支配体积
if(fn[key][j-j1] && fn[i][j1])//当已经选了主件,并且分配给附件的体积是可以得到有意义的价值的
fn[key][j] = max(fn[key][j],fn[key][j-j1]+fn[i][j1]);//更新可能的价值
}
}
}
int main(){
cin >> n >>V;
int key;
for(int i = 1;i <= n;i++){
cin >> v[i] >> w[i] >> p[i];
if(p[i] == -1)
key = i;//获得根结点的下标
}
fun(key,V);//结点, 和可支配的体积
int result = 0;
for(int j = 0;j <=V;j++)//从不同体积的最优策略中选择 价值最大的策略
result = max(result,fn[key][j]);
cout << result << endl;
return 0;
}