01背包与完全背包的基本与优化

23 篇文章 2 订阅

01背包的基本与优化

基本

DP[i][j]数组的含义:从前i个物品挑选物品放入容量为j的背包中可获取的最大价值。
那么现在就考虑递推式,首先对于所有放法,无非两种情况,放第i个物品和不放第i个物品,比较两个哪个价值更大就行了。首先如果根本就放不了第i个物品,也就是第i个物品的重量直接超过了j,那么此时就只能不放i了,那么此时的值就直接等于DP[i-1][j]。如果能放,一个值就是刚才的DP[i-1][j],另一个呢就是,DP[i-1][j-第i个物品的重量]+第i个物品的价值(一定放了一个i进去,剩下的就只要考虑在前i-1个里面选择物品装剩下的容量)。取两个中更大的一个就可以了。

代码:

int dp[MAX][MAX],g[MAX],w[MAX],v;//g为物品所占容量,w为物品价值,V为背包总容量
void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=v; ++j)
        {
            if(g[i]>j)
                dp[i][j]=dp[i-1][j];
            else
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-g[i]]+w[i]);
            }
    }
    cout<<dp[n][v];
}

:对于一些背包问题的演变,有一些问题往往dp是需要进行初始化的,也就是一些立马可以得知的dp[i][j]可能是可以通过一个循环或者直接赋值,不用去递推就能得出的,也同时为下面的递推做好准备,所以具体问题要具体分析。

优化

滚动数组处理,因为dp数组的递推式都是由上一行得来的,所以只要注意遍历顺序,就可以只用一个一维数组进行递推,然后覆盖前一行。(因为我需要的是上一行的左边的元素,所以进入到新的一行时,如果是从右往左遍历的话,左边正好就是上一行左边的值,可以完成递推)

代码:

int dp[MAX],g[MAX],w[MAX],v;//g为物品所占容量,w为物品价值,V为背包总容量
void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
        for(int j=v; j>=g[i]; --j)//小于g[i]的不用管,因为就等于上一行的值,就等于自动更新并覆盖了
            dp[j]=max(dp[j],dp[j-g[i]]+w[i]);
    cout<<dp[v];
}

完全背包的基本与优化

基本

完全背包只是在01背包上做了一点变化,也就是同一类物品的数量没有上限。那么对于基本操作来说,只是在01背包上再加个循环。01背包是把放i或者不放i两个情况遍历更新一遍,完全背包就是把能放多少个i物品的情况都遍历更新一遍,直到背包容量不够。递推式可能更好理解:dp[i][j]=max(dp[i-1][j-k×i的重量]+k×i的价值)(k>=0),至于k能增加到多少,就看什么时候到达上限(背包容量已经不够)。

代码:

int dp[MAX][MAX],g[MAX],w[MAX],v;//g为物品所占容量,w为物品价值,V为背包总容量
void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=v; ++j)
        {
            for(int k=0; k*g[i]<=j; k++)
            {
                    dp[i][j]=max(dp[i][j],dp[i-1][j-k*g[i]]+k*w[i]);
            }
        }
    }
    cout<<dp[n][v];
}

优化

完全背包可以进行时间与空间的双优化。完全背包的优化一定要先理解好完全背包的基础操作和01背包的空间优化。

时间:对于算dp[i][j]过程中取k个i的情况,和算dp[i][j-i的重量]的过程中取k-1(k>=1)个i的情况是对应的。也就是dp[i][j-k×i的重量]=dp[i][j-(k-1)×i的重量]+i的价值。所以说,对于算dp[i][j]过程中,取1到k个i的所有情况的最小值,就等于算dp[i][j-i的重量]的过程中取0到k-1(k>=1)个i的情况的最小值再加一个i的价值,然而后者的最小值是已知的,即dp[i][j-i的重量]。所以只要拿dp[i][j-i的重量]加上一个i的价值和剩下的一种情况:k=0的时候的价值去作比较,就可以得出答案,也就是dp[i][j]=max(dp[i][j-i的重量]+i的价值,dp[i-1][j])。当然如果一个i都放不下的话就只能dp[i][j]=dp[i-1][j]。

空间:这里再运用和01背包相似的原理,对空间进行优化,完全背包是要已知当前行前面元素的值,所以和01背包优化的递推方向是相反的,要从左往右推,先把当前行的左边都一个个算出来,才能递推。(可能有人会问完全背包也有要用到上一行的元素的,但是完全背包这里只用到了正上方的元素,也就是当前遍历到的元素的自己,所以无论如何都是已知的)

代码:

int dp[MAX],g[MAX],w[MAX],v;//g为物品所占容量,w为物品价值,V为背包总容量
void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
        for(int j=g[i]; j<=v; ++j)//小于g[i]的不用管,因为就等于上一行的值,就等于自动更新并覆盖了
            dp[j]=max(dp[j],dp[j-g[i]]+w[i]);
    cout<<dp[v];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值