背包问题全解 <y总AcWing>

背包九讲

思想解析:闫氏DP分析法,从此再也不怕DP问题!_哔哩哔哩_bilibili

题目以及解析来源:题库 - AcWing

推荐博客:dd大牛的《背包九讲》 - 贺佐安 - 博客园 (cnblogs.com)

一、01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

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

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

解析:

/*
f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少
f[i][j]:
    1.不选第i个物品:f[i][j]=f[i-1][j]
    2.选第i个物品:f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]) (当j>v[i]时)
*/

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            if(j>=v[i]){
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
            }
        }
    }
    cout<<f[n][m];
    
    return 0;
}

解析优化使用一维空间

01背包问题空间优化的原理是:
我们其实还是进行双重循环

  1. 外层for还是用来遍历原来二维数组的每一行(虽然现在已经没有二维数组了,但是表示的还是这个意义,只不过是用一维数组一直通过外层循环将每一行的值更新)
  2. 内层循环就是在更新二维数组(同上一个括号内的说法)的每一行中的每一列的值。
  3. 因此我们还想用上一行的值得时候,就不能从前往后了,要从后往前,更新某行最后一个值的时候,其实前面的值存储的还是上一行的所有值,所以不受影响。
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m];
    
    return 0;
}

二、完全背包问题

有 N 件物品和一个容量是 V 的背包。每件物品可以无限用

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

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

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

解析:

/*
f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少
f[i][j]:
    1.不选第i个物品:f[i][j]=f[i-1][j]
    2.选第i个物品:
        f[i][j]=max(f[i-1][j], ..... ,f[i-1][j-k*v[i]]+k*w[i], ....) (当j>k*v[i]时)
        f[i][j-v[i]]=max(f[i-1][j-v[i]], ..... ,f[i-1][j-k*v[i]]+(k-1)*w[i], ....) (当j>k*v[i]时)
        两个公式相差值为w[i]
        故f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
*/

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            if(j>=v[i]){
                f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
            }
        }
    }
    cout<<f[n][m];
    
    return 0;
}

优化成一维空间

/*
f[i]表示总体积是i的情况下,总价值最大是多少

数学归纳法:
1.假设考虑前i-1个物品之后。所有的f[j]都是正确的
2.来证明:考虑完第i个物品后,所有的f[j]也都是正确的

队友某个j而言,如果最优解中包含k个v[i]
f[j-k*v[i]]
f[j-(k-1)*v[i]-v[i]]+w[i] 包含1个v[i]
...
f[j] f[j-v[i]]+w[i]
*/

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
        for(int j=v[i];j<=m;j++){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m];
    
    return 0;
}

三、多重背包问题

有 N 件物品和一个容量是 V 的背包。

第 i 件物品最多有si 件,体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,si用空格隔开,分别表示第 i 件物品的体积和价值和数量。

输出格式

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

数据范围

0<N,V≤100
0<vi,wi,si≤100

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

解析:

01背包的拓展

/*
f[i]总体积是i的情况下,最大价值是多少

1.f[i]=0
f[m]

2.f[0]=0,f[i]=-INF,i!=0
max{f[0 .... m]}

*/
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N],s[N];
int f[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i]>>s[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){
            for(int k=1;k*v[i]<=j&&k<=s[i];k++){
                f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<f[m];
    
    return 0;
}

数据范围更改为:

0<N≤1000

0<V≤2000

0<vi,wi,si≤2000

提示:本题考查多重背包的二进制优化方法。

/*
v,w 多数量物品拆成单一物品放到数组当中去

二进制拆法:
log(s)上取整
s - 1 - 2 - 4 - 8 直到为负数,将最后的s拿出来

*/
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int n,m;
int v,w,s;
int f[N];
struct Good{
    int v,w;
};
int main(){
    vector<Good> goods;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w>>s;
        for(int k=1;k<=s;k*=2){
            s-=k;
            goods.push_back({v*k,w*k});
        }
        if(s>0) goods.push_back({v*s,w*s});
    }
    for(auto good:goods)
        for(int j=m;j>=good.v;j--)
            f[j]=max(f[j],f[j-good.v]+good.w);
    
    
    cout<<f[m];
    
    return 0;
}

数据范围再更改

0<N≤1000
0<V≤20000
0<vi,wi,si≤20000

提示:本题考查多重背包的单调队列优化方法。 困难

类似题:力扣:239. 滑动窗口最大值 - 力扣(LeetCode)

/*
f[j] = f[j-v]+w, f[j-2*v]+2w, ... f[j-k*v]+w

f[j+v]=f[j]+w, f[j-v]+2*w

*/
#include<bits/stdc++.h>
using namespace std;
const int N=20010;
int n,m;
int f[N],g[N],q[N];
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        int v,w,s;
        cin>>v>>w>>s;
        memcpy(g,f,sizeof(f));
        
        for(int j=0;j<v;j++){
            int hh=0,tt=-1;
            // 经典单调队列
            for(int k=j;k<=m;k+=v){
                f[k]=g[k];
                if(hh<=tt&&k-s*v>q[hh]) hh++;
                if(hh<=tt)  f[k]=max(f[k],g[q[hh]]+(k-q[hh])/v*w);
                while(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w)   tt--;
                q[++tt]=k;
                
            }
        }
    }
    cout<<f[m];
    
    return 0;
}

四、混合背包问题

有 N 种物品和一个容量是 V 的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 si次(多重背包);

每种体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

  • si=−1 表示第 i 种物品只能用1次;
  • si=0 表示第 i 种物品可以用无限次;
  • si>0表示第 i 种物品可以使用 si 次;

输出格式

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

数据范围

0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000

输入样例

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

输出样例:

8

解析:

#include <bits/stdc++.h>
using namespace std;
int n,m,v[100010],w[100010],f[100010];
int main(){
    cin>>n>>m;
    int cnt=1;
    for(int i=1;i<=n;i++){
        int a,b,s;
        cin>>a>>b>>s;
        int k=1;
        if(s<0)s=1;
        else if(s==0)s=m/a;//把01背包和多重背包先转化成多重背包,若为完全背包,则在最优情况下,只能取总体积/该物品体积向下取整 
        while(k<=s){
            v[cnt]=a*k;
            w[cnt]=b*k;
            s-=k;
            k*=2;
            cnt++;
        }
        if(s>0){
            v[cnt]=s*a;
            w[cnt]=s*b;
            cnt++;
        }
    }//将多重背包进行二进制优化,变成01背包 
    for(int i=1;i<=cnt;i++){
        for(int j=m;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }//01背包问题
    cout<<f[m]<<endl;
    return 0;
}

五、二维费用的背包问题

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。

每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。

输入格式

第一行三个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

接下来有 N行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。

输出格式

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

数据范围

0<N≤1000
0<V,M≤100
0<vi,mi≤100
0<wi≤1000

输入样例

4 5 6
1 2 3
2 4 4
3 4 5
4 5 6

输出样例:

8

解析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Fl4116Z-1663824406641)(D:\Desktop\背包九讲.assets\image-20220921183126088.png)]

#include <bits/stdc++.h>

using namespace std;
int n, V, M;
const int N=1e3+5;
int v[N],m[N],w[N],f[N][N];

signed main(){
    cin>>n>>V>>M;
    for(int i=1;i<=n;i++) {
        cin>>v[i]>>m[i]>>w[i];
    }
    for (int i = 1; i <= n; i ++)
        for (int j = V; j >= v[i]; j --)
            for (int k = M; k >= m[i]; k --)
                f[j][k] = max(f[j - v[i]][k - m[i]] + w[i], f[j][k]);//动态转移方程,01 背包的思路
    cout<<f[V][M];
} 

六、分组背包问题

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i是组号,j是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

  • 每组数据第一行有一个整数 Si,表示第 i个物品组的物品数量;
  • 每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i个物品组的第 j 个物品的体积和价值;

输出格式

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

数据范围

0<N,V≤100
0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例:

8

题解:

#include<bits/stdc++.h>
using namespace std;

const int N=110;
int f[N][N];  //只从前i组物品中选,当前体积小于等于j的最大值
int v[N][N],w[N][N],s[N];   //v为体积,w为价值,s代表第i组物品的个数
int n,m,k;

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        for(int j=0;j<s[i];j++){
            cin>>v[i][j]>>w[i][j];
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            for(int k=0;k<s[i];k++){
                if(j>=v[i][k])     f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);  
            }
        }
    }
    cout<<f[n][m]<<endl;
}

优化成一维空间

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N],v[N],w[N];
int n,m,k;

int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        int s;
        cin>>s;
        for(int j=0;j<s;j++){
            cin>>v[j]>>w[j];
        }
        for(int j=m;j>=0;j--){
            for(int k=0;k<s;k++){
                if(j>=v[k]) f[j]=max(f[j],f[j-v[k]]+w[k]);  
            }
        }
    }
    cout<<f[m]<<endl;
}

七、背包问题求方案数

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示 方案数 模 109+7的结果。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

2

题解

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010, mod = 1000000007;

int n, m;
int f[N], g[N];
int v[N], w[N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    for (int i = 0; i <= m; i ++ ) g[i] = 1;

    for (int i = 1; i <= n; i ++ )
        for (int j = m; j >= v[i]; j -- )
        {
            int left = f[j], right = f[j - v[i]] + w[i];
            f[j] = max(left, right);

            if (left > right) g[j] = g[j];
            else if (left < right) g[j] = g[j - v[i]];
            else g[j] = g[j] + g[j - v[i]];
            g[j] %= mod;
        }

    cout << g[m] << endl;
    return 0;
}

八、背包问题求最优的物品方案

有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N1…N。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。

物品编号范围是 1…N1…N。

数据范围

0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

1 4

题解:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


int dp[1010][1010];

int n, m;

int v[1010];
int w[1010];


int main()
{
    cin >> n >> m;
    int i = 0;
    for (i = 1; i <= n; i++) {
        cin >> v[i] >> w[i];
    }

    int l = 1; int r = n;

    while (l < r) {
        swap(v[l], v[r]);
        l++; r--;
    }

    l = 1; r = n;
    while (l < r) {
        swap(w[l], w[r]);
        l++; r--;
    }

    vector<int> vvv;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            // 默认不选
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i]) {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);

            }
        }
    }

    for (int i = n; i >= 1; i--) {
        if (m >= v[i] && dp[i][m] == dp[i - 1][m - v[i]] + w[i]) {
            //可以选
            cout << n+1-i << " ";
            m = m - v[i];
        }
    }


    return 0;
}

九、有依赖的背包问题

困难

有 NN 个物品和一个容量是 VV 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7RA3jKG-1663824406643)(https://www.acwing.com/media/article/image/2018/10/18/1_bb51ecbcd2-QQ%E5%9B%BE%E7%89%8720181018170337.png)]

如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。

每件物品的编号是 ii,体积是 vivi,价值是 wiwi,依赖的父节点编号是 pipi。物品的下标范围是 1…N1…N。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,VN,V,用空格隔开,分别表示物品个数和背包容量。

接下来有 NN 行数据,每行数据表示一个物品。
第 ii 行有三个整数 vi,wi,pivi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1pi=−1,表示根节点。 数据保证所有物品构成一棵树。

输出格式

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

数据范围

1≤N,V≤1001≤N,V≤100
1≤vi,wi≤1001≤vi,wi≤100

父节点编号范围:

  • 内部结点:1≤pi≤N1≤pi≤N;
  • 根节点 pi=−1pi=−1;

输入样例

5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2

输出样例:

11

题解:

dfs在遍历到 x 结点时,先考虑一定选上根节点 x ,因此初始化 f[ x] [v[x] ~ m] = w[x]
在分组背包部分:
j 的范围 [ m , v[x] ] 小于v[x]则没有意义因为连根结点都放不下;
k 的范围 [ 0 , j-v[x] ],当大于j-v[x]时分给该子树的容量过多,剩余的容量连根节点的物品都放不下了;

#include<iostream>
#include<vector>
using namespace std;
int f[110][110];//f[x][v]表达选择以x为子树的物品,在容量不超过v时所获得的最大价值
vector<int> g[110];
int v[110],w[110];
int n,m,root;

int dfs(int x)
{
    for(int i=v[x];i<=m;i++) f[x][i]=w[x];//点x必须选,所以初始化f[x][v[x] ~ m]= w[x]
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        dfs(y);
        for(int j=m;j>=v[x];j--)//j的范围为v[x]~m, 小于v[x]无法选择以x为子树的物品
        {
            for(int k=0;k<=j-v[x];k++)//分给子树y的空间不能大于j-v[x],不然都无法选根物品x
            {
                f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
            }
        }
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int fa;
        cin>>v[i]>>w[i]>>fa;
        if(fa==-1)
            root=i;
        else
            g[fa].push_back(i);
    }
    dfs(root);
    cout<<f[root][m];
    return 0;
}

有依赖的背包问题是指物品之间存在依赖关系,这种依赖关系可以用一棵树来表示,要是我们想要选择子节点就必须连同其父节点一块选。
我们可以把有依赖的背包问题看成是分组背包问题,每一个结点是看成是分组背包问题中的一个组,子节点的每一种选择我们都看作是组内的一种物品,因此我们可以通过分组背包的思想去写。
但它的难点在于如何去遍历子节点的每一种选择,即组内的物品,我们的做法是从叶子结点开始往根节点做,并使用数组表示的邻接表来存贮每个结点的父子关系。

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

const int N = 110;
int n,m;
int h[N],e[N],ne[N],idx;
/*h数组是邻接表的头它的下表是当前节点的标号,值是当前结点第一条边的编号(其实是最后加入的那一条边),e数组是边的集合,它的下标是当前边的编号,数值是当前边的终点;
ne是nextedge,如果ne是-1表示当前结点没有下一条边,ne的下标是当前边的编号,数值是当前结点的下一条边的编号,idx用于保存每一条边的上一条边的编号。
这样我们就知道了当前结点的第一条边是几,这个边的终点是那个结点,该节点的下一条边编号是几,那么邻接表就完成了
*/ 
int v[N],w[N],f[N][N]; 

void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;//该方法同于向有向图中加入一条边,这条边的起点是a,终点是b,加入的这条边编号为idx 
}

void dfs(int u){
    for(int i = h[u];i!=-1;i = ne[i]){//对当前结点的边进行遍历 
        int son = e[i];//e数组的值是当前边的终点,即儿子结点 
        dfs(son); 
        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];
    }
    //因为我们是从叶子结点开始往上做,所以如果背包容积不如当前物品的体积大,那就不能选择当前结点及其子节点,因此赋值为零 
    for(int i = 0;i<v[u];i++){
        f[u][i] = 0;
    }
}

int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m;
    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);//如果不是根节点就加入邻接表,其中p是该节点的父节点,i是当前是第几个节点
        }
    }
    dfs(root);
    cout<<f[root][m]<<endl;
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶辰 .

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值