背包九讲(仅提供代码回忆)
题集传送门(1~12)
(壹)01背包
有N个物品和一个体积为V的背包,每个物品体积为v[i],价值为w[i],问背包可收纳的最大价值。
时间复杂度O(N*V)。
1 /* 2 f[m]即为答案。 3 如果要表示恰好去体积为V的物品, 4 可以把除f[0]外全置为负无穷, 5 以保证是f[m]从f[0]推来的。 6 */ 7 #include<iostream> 8 using namespace std; 9 int N,V; 10 int v[1010],w[1010],f[1010]; 11 int main() 12 { 13 cin>>N>>V; 14 for(int i=0;i<N;i++) 15 cin>>v[i]>>w[i]; 16 for(int i=0;i<N;i++) 17 for(int j=V;j>=v[i];j--) 18 f[j]=max(f[j],f[j-v[i]]+w[i]); 19 cout<<f[V]<<endl; 20 return 0; 21 }
(贰)完全背包
有N种物品和一个体积为V的背包,每种物品体积为v[i],价值为w[i],且每种物品有无限个,问背包可收纳的最大价值。
时间复杂度O(N*V)。
1 /* 2 f[m]即为答案。 3 如果要表示恰好去体积为V的物品, 4 可以把除f[0]外全置为负无穷, 5 以保证是f[m]从f[0]推来的。 6 */ 7 #include<iostream> 8 using namespace std; 9 int N,V; 10 int v,w,f[1010]; 11 int main() 12 { 13 cin>>N>>V; 14 for(int i=0;i<N;i++) 15 { 16 cin>>v>>w; 17 for(int j=v;j<=V;j++) 18 f[j]=max(f[j],f[j-v]+w); 19 } 20 cout<<f[V]<<endl; 21 return 0; 22 }
(叁)多重背包
有N种物品和一个V体积背包,每种物品体积为v[i],价值为w[i],个数为s[i],问背包可收纳的最大价值。思路是将其01背包化。
- 基本解法,时间复杂度O(N*∑si)。
1 #include<iostream> 2 using namespace std; 3 int N,V; 4 int v,w,s,f[1010]; 5 int main() 6 { 7 cin>>N>>V; 8 for(int i=0;i<N;i++) 9 { 10 cin>>v>>w>>s; 11 for(int j=V;j>=v;j--) 12 for(int k=1; k<=s && k*v<=j ;k++) 13 f[j]=max(f[j],f[j-k*v]+k*w); 14 } 15 cout<<f[V]<<endl; 16 return 0; 17 }
- 二进制优化,时间复杂度O(N*∑ log s[i])。
1 #include<iostream> 2 using namespace std; 3 int N,V; 4 int f[2020]; 5 int v[11010],w[11010]; 6 int cnt=0; 7 //2的11次方大于2000,故开11倍 8 int main() 9 { 10 int x,y,z; 11 cin>>N>>V; 12 for(int i=0;i<N;i++) 13 { 14 cin>>x>>y>>z; 15 for(int j=1;j<=z;j<<=1) 16 { 17 v[cnt]=j*x; 18 w[cnt++]=j*y; 19 z-=j; 20 } 21 if(y) 22 { 23 v[cnt]=z*x; 24 w[cnt++]=z*y; 25 } 26 } 27 for(int i=0;i<cnt;i++) 28 for(int j=V;j>=v[i];j--) 29 f[j]=max(f[j],f[j-v[i]]+w[i]); 30 cout<<f[V]<<endl; 31 return 0; 32 }
- 优先队列优化,时间复杂度O(NV)。
具体解释及代码:https://blog.csdn.net/jack_jxnu/article/details/100119883
(肆)混合背包
以上三种背包问题的集合体,对此我们可以将其01和完全化,再分别处理。
#include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,V,x,y,z; int f[1010]; struct node { int kind; int v,w; }; vector<node> Thing; int main() { cin>>n>>V; for(int i=0;i<n;i++) { cin>>x>>y>>z; if(z>0) { for(int k=1;k<=z;k<<=1) { Thing.push_back({-1,k*x,k*y}); z-=k; } Thing.push_back({-1,z*x,z*y}); } else Thing.push_back({z,x,y}); } for(auto node:Thing) { if(node.kind==-1) //01背包 { for(int j=V;j>=node.v;j--) f[j]=max(f[j],f[j-node.v]+node.w); } else //完全背包 { for(int j=node.v;j<=V;j++) f[j]=max(f[j],f[j-node.v]+node.w); } } cout<<f[V]<<endl; return 0; }
(伍)二维费用背包
有N件物品与一个体积为V、承重为M的背包,每件物品给出体积vi、重量mi和价值wi。求背包可容纳的最大价值。
#include<cstring> #include<iostream> using namespace std; int f[110][110]; //体积为i承重为j的背包的最大容纳价值 int n,V,M; int v,w,m; int main() { cin>>n>>V>>M; for(int k=0;k<n;k++) { cin>>v>>m>>w; for(int i=V;i>=v;i--) for(int j=M;j>=m;j--) f[i][j]=max(f[i][j],f[i-v][j-m]+w); } cout<<f[V][M]<<endl; return 0; }
(陆)分组背包
有n个物品组,每组物品只能选其一,问背包可以放下的最大价值。
1 #include<cstring> 2 #include<iostream> 3 using namespace std; 4 int num,w[110],v[110]; 5 int f[110],n,V; 6 int main() 7 { 8 cin>>n>>V; 9 for(int i=0;i<n;i++) 10 { 11 cin>>num; 12 for(int j=0;j<num;j++) cin>>v[j]>>w[j]; 13 for(int j=V;j>=0;j--) 14 for(int k=0;k<num;k++) 15 if(j-v[k]>=0) f[j]=max(f[j],f[j-v[k]]+w[k]); 16 } 17 cout<<f[V]<<endl; 18 return 0; 19 }
(柒)依赖背包
有n个物品,彼此间有树形依赖关系(选子必选父),求最大价值。
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=110; 5 int head[N],edge[N],Nex[N],idx; 6 int v[N],w[N]; 7 int f[N][N]; //表示选i及其子树,体积为j的最大价值 8 int n,V; 9 10 void add(int x,int y) //加边x->y 11 { 12 edge[idx]=y; 13 Nex[idx]=head[x]; 14 head[x]=idx++; 15 } 16 void dfs(int x) 17 { 18 for(int i=head[x];i!=-1;i=Nex[i]) 19 { 20 int y=edge[i]; 21 dfs(y); 22 for(int j=V-v[x];j>=0;j--) 23 for(int k=0;k<=j;k++) 24 f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]); 25 } 26 27 //考虑到如果体积无法放下x节点,那么就无法放下任何东西,则为0 28 for(int i=V;i>=0;i--) 29 if(i<v[x]) f[x][i]=0; 30 else f[x][i]=f[x][i-v[x]]+w[x]; //注意不要max 31 } 32 int main() 33 { 34 memset(head,-1,sizeof head); 35 cin>>n>>V; 36 int rt,p; 37 for(int i=1;i<=n;i++) 38 { 39 cin>>v[i]>>w[i]>>p; 40 if(p==-1) rt=i; 41 else add(p,i); 42 } 43 dfs(rt); 44 cout<<f[rt][V]<<endl; 45 return 0; 46 }
(捌)背包问题方案数
问最优方案的解法有多少种?
1 #include<bits/stdc++.h> 2 #define inf 0x3f3f3f3f 3 #define M 1000000007 4 using namespace std; 5 const int N=1010; 6 int f[N],g[N]; 7 int n,V; 8 int main() 9 { 10 for(int i=1;i<N;i++) f[i]=-inf; 11 f[0]=0;g[0]=1; 12 //以上初始化保证f[i]表示体积恰好为i时的最大价值 13 14 cin>>n>>V; 15 for(int i=0;i<n;i++) 16 { 17 int v,w; 18 cin>>v>>w; 19 for(int j=V;j>=v;j--) 20 { 21 int sum=0,t=max(f[j],f[j-v]+w); 22 if(t==f[j]) sum+=g[j]; 23 if(t==f[j-v]+w) sum+=g[j-v]; 24 f[j]=t; 25 g[j]=sum%M; 26 } 27 } 28 29 int maxw=0,ans=0; 30 for(int i=0;i<=V;i++) maxw=max(maxw,f[i]); //求所有体积下的可得最大价值 31 for(int i=0;i<=V;i++) 32 if(f[i]==maxw) ans=(ans+g[i])%M; 33 cout<<ans<<endl; 34 return 0; 35 }
(玖)求背包问题方案
要求一个放置方案使得背包容纳价值最小。可以直接逆推回去。
1 #include<bits/stdc++.h> 2 #define inf 0x3f3f3f3f 3 using namespace std; 4 const int N=1010; 5 int v[N],w[N]; 6 int f[N][N]; 7 int n,V; 8 int main() 9 { 10 cin>>n>>V; 11 for(int i=1;i<=n;i++) 12 cin>>v[i]>>w[i]; 13 14 for(int i=n;i>0;i--) 15 for(int j=0;j<=V;j++) 16 if(j>=v[i]) f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i]); 17 else f[i][j]=f[i+1][j]; 18 19 for(int i=1;i<=n;i++) 20 if(f[i][V]==f[i+1][V-v[i]]+w[i] && v[i]<=V) //当心越界 21 { 22 V-=v[i]; 23 cout<<i<<' '; 24 } 25 return 0; 26 }