三种经典背包dp

01背包

 01背包问题,指一堆(N个)有重量(weight)和价值(value)的物品,每种有一个,有一个特定容量 ( j ) 的背包,如何装到最大价值

我们用dp[N][j]表示在第N个物品,背包还剩J时的最大价值

则状态转移方程:dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i])

其中dp[i-1][j]代表不装第i个物品

dp[i-1][j-weight[i]] + value[i]代表装第i个物品

则可得出:

void back()
{
    for(int i = 1;i <= N;++i)
    {
        for(int j = 0;j <= 999;++j)
        {
            if(j >= weight[i]) dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]);
            else dp[i][j] = dp[i-1][j];
        }
    }
}

这样的函数即可计算所有的dp值

可以进行一些优化

#include <bits/stdc++.h>

using namespace std;

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int dp[11][51] = {};
    int weight[11] = {};
    int value[11] = {};
    for(int i = 1;i < 11;++i) cin >> weight[i] >> value[i];
    for(int i = 1;i < 11;++i)
    {
        for(int j = 0;j <= 50 ;++j)
        {
            if(j >= weight[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
            else dp[i][j] = dp[i-1][j];
        }
    }
    cout << dp[10][50];
    return 0;
}

因为在循环过程中会有i-1,所以要从0开始存储

现在降维,将dp转换为一维,这样就可以从0开始了,即dp[j] = max(dp[j],dp[j-weight[i]])

在进行一些优化得出:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int dp[51] = {};
    int weight[10] = {};
    int value[10] = {};
    for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i];
    for(int i = 0;i < 10;++i)
    {
        for(int j = 50;j >= weight[i];--j)
        {
            dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);
        }
    }
    cout << dp[50];
}
/*
10 50
20 100
30 120
5 15
15 40
25 70
40 180
12 35
8 20
35 105
*/

答案是:230 

完全背包

根0-1背包不同的是:物品可以放入多次

先给代码:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int dp[51] = {};
    int weight[10] = {};
    int value[10] = {};
    for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i];
    for(int i = 0;i < 10;++i)
    {
        for(int j = weight[i];j < 51;++j)
        {
            dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);
        }
    }
    cout << dp[50];
}
/*
10 50
20 100
30 120
5 15
15 40
25 70
40 180
12 35
8 20
35 105
*/

答案是:250 

可以看出与0-1背包只差了一个循环的顺序

为什么变换以下顺序就可以解决完全背包问题了,而二维时无论顺序如何都是0-1背包?

从小到大遍历时:当遍历到物品x时,如果dp[ j ]加入了 x,等到 j 遍历到 2*j 时,

dp[2*j] == dp[j] + value[j],所以会再次添加,而从大到小则不会这样

从大到小遍历时:dp[2*j] == dp[j] + value[j]由于dp[j]还没有被计算过,为0,也就是说,物品不会重复添加。

而对于二维来说:因为有 i 的维度存在,当遍历到 2*j 时,dp[x][2*j] != dp[x-1][j] + value[j],防止了这种情况的出现

多重背包

也是0-1背包的变体,根其他背包不同的是,一个物品不在有一个或无限个,而是Ki个,即每个物品有固定的个数

这种问题的解决方法是将多重背包转换为0-1背包,在数组种多次加入物品即可;

或者,在循环种加入一层数量的循环即可

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int dp[51] = {};
    int weight[10] = {};
    int value[10] = {};
    int nums[10] = {};
    for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i] >> nums[i];
    for(int i = 0;i < 10;++i)
    {
        for(int j = 50;j >= weight[i];--j)
        {
            for(int k = 1;k <= nums[i] && k*weight[i] <= j;++k)
            dp[j] = max(dp[j],dp[j-k*weight[i]] + k*value[i]);
        }
    }
    cout << dp[50];
}
/*
10 1000 2
20 100 1
30 120 1
5 15 1
15 40 1
25 70 1
40 180 1
12 35 1
8 20 1
35 105 1
*/
  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值