完全背包问题相对于0-1背包问题其改变在于我们对每个物品的可选次数不再是一次,而是无限次,那么我们很容易想到在0-1背包问题的基础上(还不知道0-1背包问题的请看这篇博客),多加一层关于物品次数的循环就好了。解答如下:
(物品有i种,容量为j,k为选择的次数)
public void solve()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=W; j++)
{
dp[i][j]=-1;
for(int k=0; k*w[i]<=j; k++)
dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
}
}
时间复杂度:代码的时间复杂度都是:O(nW2)O(nW ^2)
同样,和0-1背包问题一样,我们也可以对其进行时间和空间的优化
时间复杂度的优化主要是减少一层循环,即k层循环,而这主要运用到的是数学上的推导,推导如下。
时间优化:
所以源代码可变为:
public void solve()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=W; j++)
{
dp[i][j]=dp[i-1][j];
if(w[i]<=j)
dp[i][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
空间优化主要是将其二维数组变为一维数组,这里主要是运用滚动数组的知识,而正着循环主要是因为表示同个物品取了多次。同时注意和0-1背包问题的空间优化作比较。
空间优化:
其实只需要最后保存dp[n][W]dp[n][W]就可以了,并且由递推公式p[i][j]=max{dp[i−1][j],dp[i][j−w[i]]+v[i]}p[i][j]=max{dp[i−1][j],dp[i][j−w[i]]+v[i]}可以知,我们在求dp[i][j]只依赖于dp[i][0-j]和dp[i-1][j]。即我们在求dp数组的第i行时我们只需要知道dp[i][0]dp[i][0]和第i−1i−1行就可以了。这样我们可以使用滚动数组来节省空间,即定义一个两行的数组滚动的循环使用这两行。
public void solve()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=W; j++)
{
dp_roll[i&1][j]=dp_roll[(i-1)&1][j];//&1相当于%2
if(j>=w[i])
dp_roll[i&1][j]=max(dp_roll[i&1][j],dp_roll[i&1][j-w[i]]+v[i]);
}
}
在完全背包问题中我们的dp[i][j]dp[i][j]只使用了上一行的一个元素dp[i−1][j]dp[i−1][j]所以可以直接用一维数组实现。
public void solve()
{
for(int i=1; i<=n; i++)
for(int j=w[i]; j<=W; j++)
dp_reuse[j]=max(dp_reuse[j],dp_reuse[j-w[i]]+v[i]);
}
初学者建议先把不做优化的情况先记住,等到在不同题型中运用熟练后,再考虑后续两种优化。
觉得还行,点赞加关注,再看不迷路!