多重背包问题:就是每个物品都有 有限 个,让我们求出最大价值。
那么对于第 i 个物品,也是两种情况。
1、不选第 i 个物品,那么 f [ i ] [ j ] = f [ i - 1 ] [ j ] 。
2、 选第 i 个物品,那么
选 1 个,f [ i - 1 ] [ j - vi ] + wi
选 2 个,f [ i - 1 ] [ j - 2vi ] + 2wi
……以此类推
选 k 个,f [ i - 1 ] [ j - kvi ] + kwi
那么综合一下就是 f [ i ] [ j ] = max ( f [ i ] [ j ] , f [ i - 1 ] [ j - kvi ] + kwi )。 (k 取 0、1、……、s[i]);
这种数据范围数据很小,我们可以使用朴素做法。
#include<iostream> using namespace std; const int N=110; int f[N][N]; int v[N],w[N],s[N]; int main() { int n,m; 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=0;j<=m;j++) for(int k=0;k<=s[i] && v[i]*k<=j;k++) // 取的数量<=物品数量 取的物品总体积不超过背包容量 f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k); cout<<f[n][m]<<endl; return 0; }
当数据范围是这样时1000*2000*2000一定会超时,这时候就需要用到多重背包的二进制优化。
说到优化,我们先说一个很简单的小题目,来让大家理解一下。
有1000个苹果,10个箱子,怎么个放法,不论我拿多少个苹果,都能成箱成箱的拿。
那么我们可以先将所有的物品,分组别,然后按组别使用01背包问题即可。
#include<iostream> using namespace std; const int N=12010;//1000个物品 每个物品 log1200 1000*12=12000 int n,m; int v[N],w[N]; int f[N]; int main() { cin>>n>>m; int cnt=0;//分组的组别 for(int i=1;i<=n;i++) { int a,b,s; cin>>a>>b>>s; int k=1;//组别里的个数 while(k<=s) { cnt++;//组别先增加 v[cnt]=a*k;//整体体积 w[cnt]=b*k;//整体价值 s-=k;// s减少 k*=2;// 组别个数增加 } if(s)//剩下的最后一组 余数 { cnt++; v[cnt]=a*s; w[cnt]=b*s; } } n=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]); cout<<f[m]<<endl; return 0; }