什么是0-1背包问题?
有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。
定义一个二维数组 dp 存储最大价值,其中 dp[ i ][ j ] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:
- 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i - 1 件物品的最大价值,dp[ i ][ j ] = dp[ i - 1 ][ j ]。
- 第 i 件物品添加到背包中,dp[ i ][ j ] = dp[ i - 1 ][ j - w ] + v。
第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为:
// W 为背包总体积
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
int[][] dp = new int[N + 1][W + 1];
for (int i = 1; i <= N; i++) {
int w = weights[i - 1], v = values[i - 1];
for (int j = 1; j <= W; j++) {
if (j >= w) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[N][W];
}
2.空间优化
将二维数组降为一维数组,一维数组就必须要逆序遍历。因为:f[i][v]与f[i - 1][v]以及f[i - 1][v - c[i]]有关的。当我们逆序遍历的时候,我们数组前一部分,是上一轮i - 1遍历的结果,刚好符合要求;而如果我们顺序遍历的话,我们就会把i - 1轮的数据给覆盖掉。即:数组前面的f[0] f[1] … f[v - 1] 都已经改变过,里面存的都不是 i - 1时刻的值。因此,必须采用逆序遍历的方式。
3.举一个例子
设有3件物品 ,背包能容纳的总重量为10
i=1,2,3
物品号 重量(c) 价值(w)
i=1 4 5
i=2 7 9
i=3 5 6
f[v]=max{
f[v],f[v-c[i]]+w[i] }
如果v是顺序递增 i=1时,v=4~10 (因为v要至少大于等于c[i]嘛 不然减出个负数没意义)
原先的: f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=0 f[5]=0 f[6]=0 f[7]=0
f[8]=0 f[9]=0 f[10]=0
----------------------