01背包
有一个体积为V的背包,有n个物品,每个物品都有体积vi和价值wi,在背包体积范围内,求能桌下的最大价值。
这个问题中每个物品只能用一次。
设dp[i][j]表示用前i个物品装体积为j的背包。
那么第i个物品要么装要么不装:
1、如果不装,第i个物品和没有一样,dp[i][j]=dp[i-1][j]
2、如果装,减去第i个物品的体积,加上第i个物品的价值,那么它还是和没有一样。。dp[i][j]=dp[i-1][j-v[i]]+w[i];
我们只需取这两种方案的最大值,就算是解决了。dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
时间复杂度是O(V*M),空间也是O(V*M)
其实空间可以优化
注意观察,可以发现每个状态dp[i][j]之和dp[i-1][]有关,什么i-2,i-3都没用了
我们试着开一个一维的数组dp[M];
一维难道不会覆盖掉原来的值么?
会的,但我们优先覆盖不用的值。怎么说呢?就是我们每次只会用比j小的地方的值,比j大的地方是不用到的,所以我们只要j从后往前枚举,就不会冲突了。
dp[j]=max(dp[j],dp[j-v[i]]+w[i]) [j=N->v[i]]
for(int i= 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}
完全背包
完全背包就是每种物品有无限个的背包
这个时候我们的状态转移方程可以这么写:dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
看起来好像没区别啊?!
真的么?
仔细看看,i-1变成了i
也就是说,选择了第i个物品后,并不是转到i-1,因为每个物品有无限个,所以还是转到了i
状压一下,就是
dp[j]=max(dp[j],dp[j-v[i]]+w[i]) 【j=v[i]->N】
唯一的区别就是j枚举的方向反了过来
for(int i= 1; i<= n; i++) {
for (int j = w[i]; j <= m; j++) {
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}