背包九讲

01背包问题

题目链接

AcWing 2. 01背包问题

题目描述

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

\(i\) 件物品的体积是 \(v_i\),价值是 \(w_i\)

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

输入格式

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

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

输出格式

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

数据范围

\(0<N,V≤1000\)
\(0<v_i,w_i≤1000\)

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8
  • 时间复杂度:\(O(nV)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N],n,V,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
        {
            f[i][j]=f[i-1][j];
            if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
        }
    printf("%d",f[n][V]);
    return 0;    
}
  • 滚动数组
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N],n,V,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
        for(int j=V;~j;j--)
        {
            if(j>=v[i])f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    printf("%d",f[V]);
    return 0;
}

完全背包问题

题目链接

AcWing 3. 完全背包问题

题目描述

\(N\) 种物品和一个容量是 \(V\) 的背包,每种物品都有无限件可用。

\(i\) 种物品的体积是 \(v_i\),价值是 \(w_i\)

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

输入格式

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

接下来有 \(N\) 行,每行两个整数 \(v_i,w_i\),用空格隔开,分别表示第 \(i\) 种物品的体积和价值。

输出格式

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

数据范围

\(0<N,V≤1000\)
\(0<v_i,w_i≤1000\)

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

代码

  • 时间复杂度:\(O(n^3)\)
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N],n,V,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
            for(int k=0;k*v[i]<=j;k++)
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    printf("%d",f[n][V]);
    return 0;
}
  • 时间复杂度:\(O(n^2)\)
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N],n,V,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
        {
            f[i][j]=f[i-1][j];
            if(j>=v[i])
                f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
        }
    printf("%d",f[n][V]);
    return 0;
}
  • 滚动数组
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N],n,V,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
        {
            if(j>=v[i])
                f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    printf("%d",f[V]);
    return 0;
}

多重背包

题目链接

4. 多重背包问题

题目描述

\(N\) 种物品和一个容量是 \(V\) 的背包。

\(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)

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

输入格式

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

接下来有 \(N\) 行,每行三个整数 \(v_i,w_i,s_i\),用空格隔开,分别表示第 \(i\) 种物品的体积、价值和数量。

输出格式

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

数据范围

\(0<N≤1000\)
\(0<V≤20000\)
\(0<v_i,w_i,s_i≤20000\)

输入样例

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

输出样例:

10

代码

  • 时间复杂度:\(O(n^3)\)
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,V,v[N],w[N],s[N],f[N][N];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)scanf("%d%d%d",&v[i],&w[i],&s[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
            for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    printf("%d",f[n][V]);
    return 0;
}

二进制优化+滚动数组

  • 时间复杂度:\(O(nlogs\times v)\)
#include<bits/stdc++.h>
using namespace std;
const int N=11000;
int f[2010],v[N],w[N],n,V;
int main()
{
    scanf("%d%d",&n,&V);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int a,b,s;
        scanf("%d%d%d",&a,&b,&s);
        int k=1;
        while(k<=s)
        {
            v[++cnt]=k*a;
            w[cnt]=k*b;
            s-=k;
            k<<=1;
        }
        if(s>0)
        {
            v[++cnt]=s*a;
            w[cnt]=s*b;
        }
    }
    for(int i=1;i<=cnt;i++)
        for(int j=V;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    printf("%d",f[V]);
    return 0;
}

单调队列优化

  • 时间复杂度:\((nv)\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10;
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 g);
        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;
}

二维费用的背包问题

题目链接

8. 二维费用的背包问题

题目描述

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

每件物品只能用一次。体积是 \(v_i\),重量是 \(m_i\),价值是 \(w_i\)

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

输入格式

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

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

输出格式

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

数据范围

\(0<N≤1000\)
\(0<V,M≤100\)
\(0<v_i,m_i≤100\)
\(0<w_i≤1000\)

输入样例

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

输出样例:

8

代码

  • 时间复杂度:\(O(nvm)\)
// Problem: 二维费用的背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/8/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=105;
int n,V,M,f[N][N];
int main()
{
    cin>>n>>V>>M;
    for(int i=1;i<=n;i++)
    {
    	int v,m,w;
    	cin>>v>>m>>w;
    	for(int j=V;j>=v;j--)
    		for(int k=M;k>=m;k--)
    			f[j][k]=max(f[j][k],f[j-v][k-m]+w);
    }
    cout<<f[V][M];
    return 0;
}

分组背包

题目链接

9. 分组背包问题

题目描述

\(N\) 组物品和一个容量是 \(V\) 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 \(v_{ij}\),价值是 \(w_{ij}\),其中 \(i\) 是组号,\(j\) 是组内编号。

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

输出最大价值。

输入格式

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

接下来有 \(N\) 组数据:

每组数据第一行有一个整数 \(S_i\),表示第 \(i\) 个物品组的物品数量;
每组数据接下来有 \(S_i\) 行,每行有两个整数 \(v_{ij},w_{ij}\),用空格隔开,分别表示第 \(i\) 个物品组的第 \(j\) 个物品的体积和价值;

输出格式

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

数据范围

\(0<N,V≤100\)
\(0<S_i≤100\)
\(0<v_{ij},w_{ij}≤100\)

输入样例

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

输出样例:

8

代码

  • 时间复杂度:\(O(nVs)\)
// Problem: 分组背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/9/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

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

混合背包问题

题目链接

7. 混合背包问题

\(N\) 种物品和一个容量是 \(V\) 的背包。

物品一共有三类:

第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 \(s_i\) 次(多重背包);
每种体积是 \(v_i\),价值是 \(w_i\)

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

输入格式

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

接下来有 \(N\) 行,每行三个整数 \(v_i,w_i,s_i\),用空格隔开,分别表示第 \(i\) 种物品的体积、价值和数量。

\(s_i=−1\) 表示第 \(i\) 种物品只能用\(1\)次;
\(s_i=0\) 表示第 \(i\) 种物品可以用无限次;
\(s_i>0\) 表示第 \(i\) 种物品可以使用 \(s_i\) 次;

输出格式

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

数据范围

\(0<N,V≤1000\)
\(0<v_i,w_i≤1000\)
\(−1≤s_i≤1000\)

输入样例

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

输出样例:

8

代码

\(s\) 为可选的所有背包个数

  • 时间复杂度:\(O(nVlogs)\)

解法1:

// Problem: 混合背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/7/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1005;
int n,V,f[N];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
    {
    	int v,w,s;
    	cin>>v>>w>>s;
    	if(!s)
    		for(int j=v;j<=V;j++)f[j]=max(f[j],f[j-v]+w);
    	else
    	{
    		if(s==-1)s=1;
    		for(int k=1;k<=s;s-=k,k<<=1)
    			for(int j=V;j>=k*v;j--)f[j]=max(f[j],f[j-k*v]+k*w);
    		if(s)for(int j=V;j>=s*v;j--)f[j]=max(f[j],f[j-s*v]+s*w);
    	}
    }
    cout<<f[V];
    return 0;
}
  • 解法2
// Problem: 混合背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/7/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1005;
int n,V,f[N];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
    {
    	int v,w,s;
    	cin>>v>>w>>s;
    	if(!s)
    		s=V/v;
		if(s==-1)s=1;
		for(int k=1;k<=s;s-=k,k<<=1)
			for(int j=V;j>=k*v;j--)f[j]=max(f[j],f[j-k*v]+k*w);
		if(s)for(int j=V;j>=s*v;j--)f[j]=max(f[j],f[j-s*v]+s*w);
    }
    cout<<f[V];
    return 0;
}

背包问题求方案数

题目链接

11. 背包问题求方案数

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

\(i\) 件物品的体积是 \(v_i\),价值是 \(w_i\)

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

输出 最优选法的方案数。注意答案可能很大,请输出答案模 \(10^9+7\) 的结果。

输入格式

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

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

输出格式

输出一个整数,表示 方案数 模 \(10^9+7\) 的结果。

数据范围

\(0<N,V≤1000\)
\(0<v_i,w_i≤1000\)

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

2

代码

  • 时间复杂度:\(O(nV)\)
// Problem: 背包问题求方案数
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/11/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1005,mod=1e9+7;
int n,V,f[N],cnt[N];
int main()
{
    cin>>n>>V;
    for(int i=0;i<=V;i++)cnt[i]=1;
    for(int i=1;i<=n;i++)
    {
    	int v,w;
    	cin>>v>>w;
    	for(int j=V;j>=v;j--)
    		if(f[j]<f[j-v]+w)f[j]=f[j-v]+w,cnt[j]=cnt[j-v];
    		else if(f[j]==f[j-v]+w)cnt[j]=(cnt[j]+cnt[j-v])%mod;
    }
    cout<<cnt[V];
    return 0;
}

背包问题求具体方案

题目链接

12. 背包问题求具体方案

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

\(i\) 件物品的体积是 \(v_i\),价值是 \(w_i\)

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

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

输入格式

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

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

输出格式

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

物品编号范围是 \(1…N\)

数据范围

\(0<N,V≤1000\)
\(0<v_i,w_i≤1000\)

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

1 4

代码

  • 时间复杂度:\(O(nV)\)
// Problem: 背包问题求具体方案
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/12/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1005;
int f[N][N],n,V,v[N],w[N];
vector<int> res;
void dfs(int x,int y)
{
	if(x==n+1)return ;
	if(y>=v[x]&&f[x][y]==f[x+1][y-v[x]]+w[x])
	{
		cout<<x<<' ';
		dfs(x+1,y-v[x]);
	}
	else
		dfs(x+1,y);
}
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    for(int i=n;i;i--)
    {
    	for(int j=0;j<=V;j++)
    	{
    		f[i][j]=f[i+1][j];
    		if(j>=v[i])f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);
    	}
    }
    dfs(1,V);
    return 0;
}

有依赖的背包问题

题目链接

10. 有依赖的背包问题

\(N\) 个物品和一个容量是 \(V\) 的背包。

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

如下图所示:
image

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

每件物品的编号是 \(i\),体积是 \(v_i\),价值是 \(w_i\),依赖的父节点编号是 \(p_i\)。物品的下标范围是 \(1…N\)

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

输出最大价值。

输入格式

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

接下来有 \(N\) 行数据,每行数据表示一个物品。
\(i\) 行有三个整数 \(v_i,w_i,p_i\),用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 \(p_i=−1\),表示根节点。 数据保证所有物品构成一棵树。

输出格式

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

数据范围

\(1≤N,V≤100\)
\(1≤v_i,w_i≤100\)
父节点编号范围:

内部结点:\(1≤p_i≤N;\)
根节点 \(p_i=−1;\)

输入样例

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

输出样例:

11

代码

  • 时间复杂度:\(O(nV^2)\)
// Problem: 有依赖的背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/10/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=105;
int n,V,v[N],w[N],f[N][N],root;
vector<int> adj[N];
void dfs(int x)
{
	for(int i=v[x];i<=V;i++)f[x][i]=w[x];
	for(int y:adj[x])
	{
	    dfs(y);
		for(int i=V;i>=v[x];i--)
			for(int j=v[y];j<=i-v[x];j++)
				f[x][i]=max(f[x][i],f[x][i-j]+f[y][j]);
	}
}
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
    {
    	int p;
    	cin>>v[i]>>w[i]>>p;
    	if(p!=-1)adj[p].pb(i);
    	else
    		root=i;
    }
    dfs(root);
    cout<<f[root][V];
    return 0;
}
posted @ 2021-09-19 21:11  zyy2001  阅读(46)  评论(0编辑  收藏  举报