AcWing有依赖的背包问题

10.有依赖的背包问题

题意

有 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;
}


猜你喜欢

转载自blog.csdn.net/RunningBeef/article/details/111416061
今日推荐