代码随想录算法训练营 | 动态规划 part03

0-1背包

n 件物品和一个最多能背重量为 w 的背包。第i件物品的重量是 weight[i],得到的价值是 value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

二维数组

对于第 i 件物品,只有不放入背包和放入背包两个选择;不放入背包,背包容量不变;放入背包,背包容量减小。
所以需要两个维度,分别表示背包容量和物品;定义二维数组为 dp[i][j]表示前 i 件物品放入背包中的价值总和;i 来表示物品、j 表示背包容量;

  1. 当背包容量 j 小于i 件物品的重量时,此时价值总和就是前 i - 1 件物品中放入背包的价值总和 dp[i - 1][j]

  2. 当背包容量 j 大于i 件物品的重量时,
    2.1. 第 i 件物品不放入背包;此时价值总和就是前 i - 1 件物品中放入背包的价值总和: dp[i - 1][j]
    2.2. 第 i 件物品放入背包;此时价值总和就是前 i - 1 件物品中放入背包(剩余容量 j - w[i] )的价值总和加上第 i 件物品的价值: dp[i - 1][j - w[i]] + v[i]

    最后,前 i 件物品放入背包中的价值总和为:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i])

例子:
背包可容纳重量 W = 6;

物品重量w价值v
123
241
335
412
543

dp table

-0123456
00000000
10033333
20033334
30035588
402357810
502357810

最后,dp[5][6] = 10,即前5件物品放入背包中的最大价值为10

一维数组

递推公式:dp[j] = max(dp[j], dp[j - w[i]] + v[i])
在遍历背包时要倒序遍历,因为正序遍历会让一个物品被多次放入;
比如:第 1 个物品,在背包容量为 4 的时候,被放入两次;

-0123456
初始状态00000000
10033600

46. 携带研究材料

卡码网46. 携带研究材料

题目描述
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

输入描述 第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。
第二行包含 M 个正整数,代表每种研究材料的所占空间。
第三行包含 M 个正整数,代表每种研究材料的价值。

输出描述 输出一个整数,代表小明能够携带的研究材料的最大价值。

输入示例 6 1 2 2 3 1 5 2 2 3 1 5 4 3

输出示例 5
提示信息 小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。

数据范围: 1 <= N <= 5000 1 <= M <= 5000 研究材料占用空间和价值都小于等于 1000用

二维数组

#include <iostream>
#include <vector>
using namespace std;

int package(int& m, int& n, vector<int>& weight, vector<int>& value) {
    vector<vector<int>> dp(m + 1, vector<int>(n + 1));
    for (int i = 1; i < m + 1; i++) {
        for (int j = 0; j < n + 1; j++) {
            if (j < weight[i-1]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);
            }
        }
    }
    // for (int i = 0; i < m + 1; i++) {
    //     for (int j = 0; j < n + 1; j++) {
    //         cout << dp[i][j] << " ";
    //     }
    //     cout << endl;
    // }
    return dp[m][n];
}

int main() {
    int m; // 材料种类
    int n; // 行李空间
    // m = 6; n = 1;
    cin >> m >> n;
    vector<int> weight(m); // 每种研究材料的所占空间
    vector<int> value(m); // 每种研究材料的价值
    for(int i = 0; i < m; ++i) {
        cin >> weight[i];
    }
    for(int j = 0; j < m; ++j) {
        cin >> value[j];
    }
    int res = package(m, n, weight, value);
    cout << res << endl;
    return 0;
}

一维数组

int package(int& m, int& n, vector<int>& weight, vector<int>& value) {
    vector<int> dp(n + 1);
    for (int i = 1; i < m + 1; i++) {
        for (int j = n; j >= weight[i - 1]; j--) { // 逆序
            dp[j] = max(dp[j], dp[j - weight[i - 1]] + value[i - 1]);
        }
    }
    //for (int j = 0; j < n + 1; j++) {
    //    cout << dp[j] << " ";
    //}
    //cout << endl;
    return dp[n];
}

416. 分割等和子集

416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

将每一个子集都看作一个背包,每一个数字都有放入和不放入两种选择,想让子集和等于一半的所有元素和,所以背包容量取总元素和的一半;
因为元素都是正整数,所以每个子集和必须时正整数,对于总元素和为奇数时,不满足子集和为正整数,即不存在将数组分割成两个子集,且两个子集的元素和相等;

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if (sum % 2 != 0) {
            return false;
        }
        int m = nums.size() + 1;
        int n = sum / 2 + 1;
        vector<int> dp(n);
        for (int i = 1; i < m; i++) {
            for (int j = n - 1; j >= nums[i - 1]; j--) {
                dp[j] = max(dp[j], dp[j - nums[i - 1]] + nums[i - 1]);
            }
        }
        // for (int j = 0; j < n; j++) {
        //     cout << dp[j] << " ";
        // }
        // cout << endl;
        return dp[n - 1] == sum / 2;
    }
};
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值