01背包
定义:
有N件物品和一个容量为V的背包。每种物品只有一件,选或者不选,第i件物品的体积是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
实现:
令dp[i][j]表示选择前i件物品,背包体积为j时的最大价值。
因为01背包中每件物品只有选和不选两种状态,所以状态方程比较容易实现:
①如果不放第i件物品,则dp[i][j]=dp[i-1][j]直接转移。
②如果放第i件物品,则dp[i][j]=dp[i-1][j-w[i]]+v[i]。
可以看出,当背包容量小于当前物品体积时,只能放弃此物品。反之,可以选择放或不放。
此方法的时间和空间复杂度都为O(N*V)。时间复杂度已经不能再优化了,但空间复杂度可以优化到O(V)。
可以看到,dp[i][j]的更新只涉及到dp[i-1][],也就是说dp[i][j]的更新只涉及到dp[][]数组中第(i-1)行,(i-1)行之前的数据就没有什么用了。
可以看到dp[]数组降成了一维。与此同时需要注意的是,第二层循环成了逆序。因为旧方法中更新dp[i][]数组第i行时,dp[i-1][]的值都不会再改变了,如果第二层循环为顺序的话,那么在更新dp[j]时用到的dp[j-w[i]]在之前很可能已经被改变。所以为了保证在优化空间复杂度的同时保证正确性,把第二层循环改成逆序。
完全背包
定义:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
实现:
令dp[i][j]表示选择前i种物品,背包体积为j时的最大价值。
与01背包不同,现在物品都有无限件,所以完全背包的状态应该为取0件,取1件,取2件,……。可以得到01背包类似的状态转移方程:dp[i][j]= dp[i-1][j-k*w[i]]+k*v[i]。
由于每件物品无限件,使得算法复杂度变大。
那么如何实现优化呢?
上面已经说过,01背包的优化之所以第二层循环是逆序根本目的就是保证每件物品只取一次。然而完全背包是每件物品取无限次,所以🙃
与01背包优化算法唯一不同的就是第二层循环成了顺序。如果说01背包逆序是为了防止更新,那么完全背包顺序就是让它更新,以达到每件物品无限件的效果。
多重背包
定义:
有N种物品和一个容量为V的背包。第i种物品最多有num[i]件可用,每件体积是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
实现:
多重背包的每种物品既不是一个也不是无限个,而是有限个。
一.我们可以把多重背包直接当成01背包实现,只不过有的物品属性一样而已。在多重背包中,第一种物品有5件,可以任意取,那么在01背包中就是有5件 第一种物品,每件物品可以选或者不选。
二.我们知道若干个数值为2^n的数相加可以表示一定范围内的所有数。
进行组合,可以表示1-3;
进行组合,可以表示1-7。
这就是常说的多重背包二进制优化,我们可以利用这种思想去优化多重背包,然后利用01背包实现。
如果我们第一种物品有3件,每件物品(体积为v,价值为w),那么二进制优化后,可以这样表达,我们有两件物品,第一件物品(v,w),第二件物品(2v,2w)。
如果我们第一种物品有5件,每件物品(体积为v,价值为w),那么二进制优化后,可以这样表达,我们有三件物品,第一件物品(v,w),第二件物品(2v,2w),第三件物品(2v,2w)。
void Zero_onepack(int w,int v) { for(int i=V;i>=w;i--) dp[i]=max(dp[i],dp[i-w]+v); } void Completepack(int w,int v) { for(int i=w;i<=V;i++) dp[i]=max(dp[i],dp[i-w]+v); } void Multipack(int w,int v,int num) { if(w*num>=V) Completepack(w,v); //这个物品可以直到取完,相当于完全背包 else //二进制优化后01背包 { int k=1; while(k<num) { Zero_onepack(k*w,k*v); num-=k; k<<=1; } Zero_onepack(num*w,num*v); } }
分组背包:
定义:
有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是v[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
实现:
令dp[i][j]表示前i组物品,背包体积为j时的最大价值dp[i][j]。
分组背包有两种状态,在一组中选一个或者一个都不选。那么状态转移方程为dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])。
优化成一维,注意循环顺序:
for 所有的组k
for j=V..0
for 所有的i属于组k
dp[j]=max(dp[j],dpf[j-w[i]]+v[i])