【ACWing】10. 有依赖的背包问题

题目地址:

https://www.acwing.com/problem/content/10/

N N N个物品和一个容量是 V V V的背包。物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。每件物品的编号是 i i i,体积是 v i v_i vi,价值是 w i w_i wi,依赖的父节点编号是 p i p_i pi。物品的下标范围是 1 , … , N 1,…,N 1,,N。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。

输入格式:
第一行有两个整数 N N N V V V,用空格隔开,分别表示物品个数和背包容量。接下来有 N N N行数据,每行数据表示一个物品。第 i i i行有三个整数 v i , w i , p i v_i,w_i,p_i vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。如果 p i = − 1 p_i=−1 pi=1,表示根节点。数据保证所有物品构成一棵树。

输出格式:
输出一个整数,表示最大价值。

数据范围:
1 ≤ N , V ≤ 100 1≤N,V≤100 1N,V100
1 ≤ v i , w i ≤ 100 1≤v_i,w_i≤100 1vi,wi100
节点编号范围:
内部结点: 1 ≤ p i ≤ N 1≤p_i≤N 1piN
根节点 p i = − 1 p_i=−1 pi=1

思路是动态规划,设 f [ u ] [ j ] f[u][j] f[u][j]是以 u u u为树根的子树里,包含树根的所有方案中,总体积不超过 j j j的那些方案的最大价值。那么这些方案可以按照第 i i i个子树选不选来分类,这样问题成为一个 0 − 1 0-1 01背包问题,对于每个子树 x x x,先递归求解 f [ x ] f[x] f[x],接着枚举该子树耗用的体积(这里可以看成是有体积分别是 0 , 1 , . . . , j − v u 0,1,...,j-v_u 0,1,...,jvu,价值分别是 f [ x ] [ 0 ] , f [ x ] [ 1 ] , . . . , f [ x ] [ j − v u ] f[x][0],f[x][1],...,f[x][j-v_u] f[x][0],f[x][1],...,f[x][jvu]的物品,我们枚举这些个物品选哪个),来更新 f [ u ] [ j − v u ] f[u][j-v_u] f[u][jvu]。总的来说,这个问题像是做一个分组背包,每个子树看成是一个组,然后这个组里有若干种体积不同的物品,我们只能选其中一个(选体积 0 0 0的那个物品的时候,相当于不选这个组的物品)。枚举 f [ u ] f[u] f[u]的体积的时候可以模仿 0 − 1 0-1 01背包的方式,从大到小来枚举,这样可以只开一维,以节省空间。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110;

int n, m;
int v[N], w[N];
// 邻接表建树
int h[N], e[N], ne[N], idx;
// f[u][j]的定义是,在以u为树根的子树里选,包含u的且总体积不超过j的所有方案中,最大价值是多少
int f[N][N];

void add(int a, int b) {
    
    
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 求解f[u]向量
void dfs(int u) {
    
    
	// 枚举每个子树
    for (int i = h[u]; i != -1; i = ne[i]) {
    
    
        int son = e[i];
        // 对于所有的k,递归求解f[son][k]
        dfs(e[i]);
		
		// 枚举体积
        for (int j = m - v[u]; j >= 0; j--)
            for (int k = 0; k <= j; k++)
                f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
    }

    for (int i = m; i >= v[u]; i--) f[u][i] = f[u][i - v[u]] + w[u];
    // 当i < v[u]的时候,树根不能取,也就不存在合法方案,赋值为0
    for (int i = 0; i < v[u]; i++) f[u][i] = 0;
}

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

    dfs(root);

    cout << f[root][m] << endl;

    return 0;
}

时间复杂度 O ( N V 2 ) O(NV^2) O(NV2),空间 O ( N V ) O(NV) O(NV)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114298691