背包问题第二讲--完全背包问题

完全背包问题相对于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]);
}

初学者建议先把不做优化的情况先记住,等到在不同题型中运用熟练后,再考虑后续两种优化。
觉得还行,点赞加关注,再看不迷路!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值