背包九讲之七:有依赖的背包问题
有 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 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int array_size = 101; 5 /* 6 构建邻接表: 7 用数组存储顶点,第n个顶点表示第n个物品,并指向此顶点的第一个弧结点; 8 弧结点存储此顶点子结点的下标,并指向下一个弧结点 9 */ 10 class ArcNode { //弧结点 11 public: 12 int adjvex; //弧头:下一个顶点位置 13 ArcNode* next; //指向下一个弧结点 14 }; 15 typedef class VNode { //顶点 16 public: 17 int v, w; //物品的体积,价值 18 ArcNode* first; //第一个弧结点 19 }AdjList[array_size]; 20 AdjList list; 21 int N, V, f[array_size][array_size];//物品数,总体积,f[i][j]表示第i个结点体积最大为j时最大价值 22 void add(int u, int p) { ////该方法同于向有向图中加入一条边, 23 ArcNode* arc = new ArcNode[1]; 24 arc->adjvex = u; 25 arc->next = list[p].first; 26 list[p].first = arc; 27 } 28 void dfs(int u) { 29 for (ArcNode* arc = list[u].first; arc; arc = arc->next) {//对当前结点的边进行遍历 30 int son = arc->adjvex; //当前边的邻接点即儿子结点 31 dfs(son); 32 //遍历背包的容积,因为我们是要遍历其子节点,故减去父结点所占体积 33 //这个时候当前结点我们看成是分组背包中的一个组,子节点的每一种选择我们都看作是组内一种物品, 34 //所以是从大到小遍历 35 for (int j = V - list[u].v; j >= 0; --j) 36 for (int k = 0; k <= j; ++k) 37 f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]); 38 } 39 //加上刚刚默认选择的父节点价值 40 for (int i = V; i >= list[u].v; --i) 41 f[u][i] = f[u][i - list[u].v] + list[u].w; 42 //如果背包容积不如当前物品的体积大,那就不能选择当前结点及其子节点 43 for (int i = 0; i < list[u].v; ++i) 44 f[u][i] = 0; 45 } 46 int main() { 47 int root; 48 cin >> N >> V; 49 for (int i = 1; i <= N; ++i) { 50 int p; 51 cin >> list[i].v >> list[i].w >> p; 52 if (p == -1) 53 root = i; 54 else 55 //将子结点添加到此结点的第一个位置 56 add(i, p); 57 } 58 dfs(root); 59 cout << f[root][V]; 60 }