背包问题是动态规划最基础的基础,这里本蒟蒻讲解一下几类背包问题。
01背包
题目
有N件物品和一个容量为V的背包,每种物品只有一个,放入第i件物品的费用是Ci,价值是Wi。
求将若干个物品装入背包得到的最大价值。(总费用不能超过总容量V)
解析
最基本的背包,每种物品只有一个,可放可不放。
很容易可以定义出状态F[i][j]表示前i件物品放入一个容量为j的背包的最大价值。
状态转移方程是F[i][j]=max(F[i-1][j],F[i-1][v-Ci]+Wi)(即不放与放)。
所以可以得到代码:
//F数组初值为0 for(int i=1;i<=N;i++) { for(int j=0;j<C[i];j++) F[i][j]=F[i-1][j]; for(int j=C[i];j<=V;j++) F[i][j]=max(F[i-1][j],F[i-1][j-C[i]]+W[i]); }
很显然时间复杂度已将无法优化了,考虑优化空间复杂度。
第二维显然无法舍去,考虑舍去第一维。
定义状态F[j]表示原来的F[i][j],舍去的i将在循环中体现。
F[i][j]是由F[i-1][j]与F[i-1][j-Ci]推导出的,所以我们需要保证F[j]也是由它们推导而出。
事实上,如果我们逆着做j循环,即j从V到0循环,计算F[j]时,F[j-Ci]的值就是F[i-1][j-Ci]的值。
所以我们可以将其优化成一维数组。
关于F数组初值的问题:
如果要求恰好装满背包,那么初始化时F[0]=0,其它均为负无穷,因为除了容量为0的背包可以在什么都不装时价格为0,其它容量的背包都不行(即不合法)。
如果没有要求恰好装满背包,那么初始化时F数组全部为0,因为任何容量的背包都可以不装任何物品价格为0。
Code
//F数组初值为0 for(int i=1;i<=N;i++) for(int j=V;j>=C[i];j--) F[j]=max(F[j],F[j-C[i]]+W[i]