【算法】01背包

简介:

背包问题可以描述为:给定⼀组物品,每种物品都有⾃⼰的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最⾼。


我们看一个01背包的例子:

例如:有一个背包体积为5
在这里插入图片描述
我们有非常多的选择方案,
但价值最多的方案只有选择体积为4的2号物品才可以得到最大价值,虽然背包没有装满。

那么01背包的意思是什么呢?
所有的物品只有1份,取走就是0份,所以理解为01背包
反之,若是所有物品都是无限份,那么就是完全背包。

例题:

题目链接:01背包

在这里插入图片描述

思路:

第一问:

按照动态规划的套路来走,分为

  1. 确定状态表示:
  2. 确定状态转移方程:
  3. 初始化:
  4. 确定填表顺序:
  5. 返回答案

我们先尝试使用一般的思路进行解决,以某一位置为结尾进行分析。
dp[i]:在前i个物品中挑选,所有的选法中最大的价值。
以上边的状态表示分析转移方程是得不到的。
原因在于:
在这里插入图片描述


所以我们通常面对01背包问题会有一个比较通用的状态表示:

dp[i][j] 表⽰:从前 i 个物品中挑选,总体积不超过 j ,所有的选法中,能挑选出来
的最⼤价值。

分析状态方程:
在这里插入图片描述
初始化:
我们多加⼀⾏,⽅便我们的初始化,此时仅需将第⼀⾏初始化为 0 即可。
因为根据我们的状态转移方程,i == 0就是没有物品时,理所当然价值为0。
那么需不需要多加一列呢?为了方便我们可以多加一列,避免了下标的不对应。

填表顺序:
分析一下状态方程即可得到大方向是由上到下即可。

第二问:

与第一问几乎一样,但需要改几个点。
注意:由于dp[i][j]可能存在非法的情况,也就是并没有体积==j时的情况,我们将这种情况设置为-1。
为什么不设置为0?状态方程那会有分析。

dp[i][j] 表⽰:从前 i 个物品中挑选,总体积== j ,所有的选法中,能挑选出来的最⼤价值。

分析状态方程:
在这里插入图片描述

初始化:
我们多加⼀列,由状态表示得:无论由几个物品,但是我们都需要==j,因此都填为0。
也要多加一行,也是由状态表示得:从 j > 0 开始,由于物品始终为0的缘故,所以都是非法情况,故都为-1。

填表顺序:
分析一下状态方程即可得到大方向是由上到下即可。

代码:

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

int main() 
{
    // 第一问
    // 初始化dp表与w和v数组
    int n, V;
    cin >> n >> V;
    vector<int> w(n + 1);
    vector<int> v(n + 1);
    vector<vector<int>> dp1(n + 1, vector<int>(V + 1));
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    // 填表
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= V; j++)
        {
            dp1[i][j] = dp1[i-1][j];
            if (j >= v[i])
            {
                dp1[i][j] = max(dp1[i][j], w[i] + dp1[i-1][j-v[i]]);
            }
        }
    }
    cout << dp1[n][V] << endl;
    // 第二问
    vector<vector<int>> dp2(n + 1, vector<int>(V + 1));
    for (int i = 1; i <= V; i++) dp2[0][i] = -1;
    // 填表
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= V; j++)
        {
            dp2[i][j] = dp2[i-1][j];
            if (j >= v[i] && dp2[i-1][j-v[i]] != -1)
            {
                dp2[i][j] = max(dp2[i][j], w[i] + dp2[i-1][j-v[i]]);
            }
        }
    }

    cout << (dp2[n][V] == -1 ? 0 : dp2[n][V]);

    return 0;    
}

优化:

利用滚动数组来进行空间优化。
当我们填表的位置为红圈时,对钩为我们可能会用到的位置,因此,我们可以使用一行数组进行优化空间,每次覆盖式的填充。
在这里插入图片描述
注意:
这样我们就需要从右往左进行优化,避免造成覆盖。
另外,优化的代码我们可以直接在源代码中进行修改,只需将dp表中的横坐标全部删掉即可!

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

int main() 
{
    // 第一问
    // 初始化dp表与w和v数组
    int n, V;
    cin >> n >> V;
    vector<int> w(n + 1);
    vector<int> v(n + 1);
    vector<int> dp1((V + 1));
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    // 填表
    for (int i = 1; i <= n; i++)
        for (int j = V; j >= v[i]; j--)
            dp1[j] = max(dp1[j], w[i] + dp1[j-v[i]]);
    cout << dp1[V] << endl;
    // 第二问
    vector<int> dp2(V + 1);
    for (int i = 1; i <= V; i++) dp2[i] = -1;
    // 填表
    for (int i = 1; i <= n; i++)
        for (int j = V; j >= v[i]; j--)
            if (dp2[j-v[i]] != -1)
                dp2[j] = max(dp2[j], w[i] + dp2[j-v[i]]);

    cout << (dp2[V] == -1 ? 0 : dp2[V]);

    return 0;    
}

完~

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
01背包问题是指有n个物品和一个容量为V的背包,每种物品只有一个,可以选择放或不放,要求在不超过背包容量的前提下,尽可能多地装入物品。贪心算法可以用来解决一部分01背包问题,即每个物品只能选择全部装入或不装入的情况。 具体的贪心策略是按照物品单位重量的价值从大到小的顺序,依次将物品装入背包中,直到背包无法再装下为止。这种贪心策略称为单位重量价值排序策略。 下面是一个简单的实现示例: ```python def knapsack_01(weights, values, capacity): n = len(weights) # 计算每个物品的单位重量价值 vpw = [(values[i] / weights[i], weights[i], values[i]) for i in range(n)] # 按照单位重量价值从大到小排序 vpw.sort(reverse=True) # 背包当前剩余容量 remain = capacity # 装入背包的总价值 total_value = 0 # 依次将单位重量价值最大的物品装入背包中 for i in range(n): if remain >= vpw[i][1]: remain -= vpw[i][1] total_value += vpw[i][2] else: total_value += remain * vpw[i][0] break return total_value ``` 其中,weights和values分别为n个物品的重量和价值,capacity为背包容量。vpw是一个列表,其中每个元素都是一个三元组,分别表示物品的单位重量价值、重量和价值。在实现中,我们将vpw按照单位重量价值从大到小排序,然后依次将单位重量价值最大的物品装入背包中,直到背包无法再装下为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值