本文已参与「新人创作礼」活动,一起开启掘金创作之路。
动态规划
分析: 令dp[i][v]表示前i件物品(1≤i≤n,0≤v≤V恰好装入容量为v的背包中所能获得的最大价值。怎么求解dp[i][v]呢? 考虑对第ⅰ件物品的选择策略,有两种策略: ①不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v的背包中所能获得的最大价值,也即dp[i-1][v]. ②放第i件物品,那么问题转化为前i-1件物品恰好装入容量为ⅴ-w[i] 的背包中所能获得的最大价值,也即dp[i-1][v-w[i]]+c[i]. 由于只有这两种策略,且要求获得最大价值,因此 dp[i][v]=max{ dp[i-1][v], dp[i-1][v-w[i]]+c[i] } (1≤i≤n,w[i]≤v≤V) 上面这个就是状态转移方程。注意到dp[i][v]只与之前的状态dp[i-1][]有关,所以可以枚举i从1到n,v从0到V,通过边界dp[0][v]=0(0≤v≤V)(即前0件物品放入任何容量v的背包中都只能获得价值0)就可以把整个dp数组递推出来。而由于dp[i][v]表示的是恰好为ⅴ的情况,所以需要枚举dp[i][v] (1≤i≤n,0≤v≤V),取其最大值才是最后的结果。 这里的时间复杂度和空间复杂度都是O(nV). 对于空间复杂度,我们还可以试着优化一下,你看,每个dp[i][] 只需要使用上一个dp[i-1][]的信息,前面的不再需要使用,后面的dp[i+1][]也是,只需要使用dp[i][]的信息,不再需要前面的信息,看到了这个特点,我们尝试对原来的二维数组进一步优化。现在,我们仅使用一维数组,dp[],并且所有元素全部初始化为0.然后每一轮逆着计算dp[].即从最后一个元素往前面计算。(==那为什么是从最后一个到第一个呢?因为这里的dp[v]实际上存储的是原来dp[i-1][1]到dp[i-1][v-1]和dp[i][v+1]到dp[i][V]的部分,然后每次计算一个dp[i][v],组成一个完整的数组,因为我们每一次计算的时候只需要使用(原二维数组)前一行的“前面的部分值”,这样在计算的时候需要从后面开始,才不会破坏前面的值,免得下一次计算就没法使用了。==)这样每一轮计算出来的,都是前面i个物品在不同背包大小下的最大价值。 代码:
for(int i=1;i<=n;i++){
for(int v=V;v>=w[i];v--{
dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
}
}
这时候空间复杂度优化为O(n).