背包九讲之七:有依赖的背包问题

有 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 }
posted @ 2020-03-19 18:53  谢哥在彼方  阅读(610)  评论(0编辑  收藏  举报