1. 01背包:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使价值总和最大。
对于这类问题我们我们定义f[i][j]表示在前i个物品中选总容量为j所能得到的最大价值为多少于是我们状态转移便是这样 f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
int f[N][M]; void work() { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) for(int j=w[i];j<=m;j++) f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]); for(int i=1;i<=m;i++) ans=max(f[n][i],ans); printf("%d\n",ans); }
这样一来,我们的时间复杂度就是O(nm),空间复杂度为O(nm),但我们发现f[i]的值只与f[i-1]有关,此时我们可以用滚动数组来优化空间到O(m),为f[j]=max(f[j],[j-w[i]]+v[i]);此时我们的j就要倒序枚举,因为j只会从比它小的j那转移。
int f[M]; void work() { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]); for(int i=1;i<=m;i++) ans=max(f[i],ans); printf("%d\n",ans); }
2.完全背包问题:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。第 i 种物品的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。这个问题非常类似于 01 背包问题,所不同的是每种物品有无限个。
这时我们就有一个思路,我们将01背包倒序的滚动数组做法正序,这样我们就起到了一个物品选多次的效果
int f[M]; void work() { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) for(int j=w[i];j<=m;j++) f[j]=max(f[j],f[j-w[i]]+v[i]); for(int i=1;i<=m;i++) ans=max(f[i],ans); printf("%d\n",ans); }
3.多重背包问题:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 n[i]件可用,每件费用是 c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
若是我们将东西直接拆成一个一个,再跑01背包,这势必会超时,这时我们要用到2进制的思想,因为任何一个数都可以拆成由1,2,4,8..因为他们的二进制是01的
int f[M]; void work() { memset(f,0,sizeof(f)); for(int i=n;i>=1;i--) { --g[i]; for(int b=2;b<=g[i];b<<=1) { g[i]-=b; w[++n]=w[i]*b; v[n]=v[i]*b; } if(g[i]!=0) { w[++n]=w[i]*g[i]; v[n]=v[i]*g[i]; } } for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]); for(int i=1;i<=m;i++) ans=max(f[i],ans); printf("%d\n",ans); }
4.混合三种背包问题:如果将三种背包问题混合起来。也就是说,有的物品只可以取一次(01 背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?
01 背包与完全背包的混合考虑只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是 O(nm)。
int f[M]; void work() { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) if(g[i]) for(int j=w[i];j<=m;j++) f[j]=max(f[j],f[j-w[i]]+v[i]); else for(int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]); for(int i=1;i<=m;i++) ans=max(f[i],ans); printf("%d\n",ans); }
如果是再加上多重背包,再加上有的物品最多可以取有限次,我们就只能拆分物品
5.二维费用的背包问题:二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。
费用加了一维,只需状态也加一维即可。设 f[i][v][u]表示前 i 件物品付出两种代价分别为 v和 u 时可获得的最大价值。状态转移方程就是:f[i][v][u]=max(f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i])
6.分组的背包问题:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i],价值是 w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前 k 组物品花费费用 v 能取得的最大权值,则有:f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k 。
int f[M]; void work() { memset(f,0,sizeof(f)); for(int k=1;k<=n;k++) for(int i=1;i<=w[k][0];i++) for(int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[k][i]]+v[k][i]); for(int i=1;i<=m;i++) ans=max(f[i],ans); printf("%d\n",ans); }
来源:https://www.cnblogs.com/cold-cold/p/9991315.html