背包九讲

01背包问题

题目链接

AcWing 2. 01背包问题

题目描述

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

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

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

输入格式

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

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

输出格式

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

数据范围

0<N,V1000
0<vi,wi1000

输入样例

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 种物品的体积是 vi,价值是 wi

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

输入格式

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

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

输出格式

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

数据范围

0<N,V1000
0<vi,wi1000

输入样例

4 5 1 2 2 4 3 4 4 5

输出样例:

10

代码

  • 时间复杂度:O(n3)
#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(n2)
#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 种物品最多有 si 件,每件体积是 vi,价值是 wi

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

输入格式

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

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

输出格式

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

数据范围

0<N1000
0<V20000
0<vi,wi,si20000

输入样例

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

输出样例:

10

代码

  • 时间复杂度:O(n3)
#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×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

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

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

输入格式

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

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

输出格式

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

数据范围

0<N1000
0<V,M100
0<vi,mi100
0<wi1000

输入样例

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 的背包。

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

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

输出最大价值。

输入格式

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

接下来有 N 组数据:

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

输出格式

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

数据范围

0<N,V100
0<Si100
0<vij,wij100

输入样例

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背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi

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

输入格式

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

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

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

输出格式

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

数据范围

0<N,V1000
0<vi,wi1000
1si1000

输入样例

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 件物品的体积是 vi,价值是 wi

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

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

输入格式

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

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

输出格式

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

数据范围

0<N,V1000
0<vi,wi1000

输入样例

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 件物品的体积是 vi,价值是 wi

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

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

输入格式

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

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

输出格式

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

物品编号范围是 1N

数据范围

0<N,V1000
0<vi,wi1000

输入样例

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,则必须选择物品12。这是因为25的父节点,12的父节点。

每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1N

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

输出最大价值。

输入格式

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

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

输出格式

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

数据范围

1N,V100
1vi,wi100
父节点编号范围:

内部结点:1piN;
根节点 pi=1;

输入样例

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

输出样例:

11

代码

  • 时间复杂度:O(nV2)
// 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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/15310309.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(51)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示