首页 > 编程语言 >代码随想录算法训练营第四十一天 | 0-1背包问题

代码随想录算法训练营第四十一天 | 0-1背包问题

时间:2024-06-18 17:12:46浏览次数:7  
标签:遍历 int 训练营 随想录 value 第四十一 背包 weights dp

46.携带研究材料

二维数组

题目链接 文章讲解 视频讲解

动态规划五部曲:

  1. dp[i][j]:下标i表示背包装0-i的物品(任取),j表示当前背包的最大容量,dp[i][j]表示容量为j时,装0-i的物品的最大价值
  2. 递推公式:dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
    • dp[i-1][j] 表示j的容量全部装0-i-1的物品,不装物品i的最大价值
    • dp[i-1][j-weight[i]] 表示背包空出装物品i的容量的最大价值,再加上value[i],表示容量为j时,装物品j的最大价值
    • 两者取最大值,就是dp[i][j]的最大价值,如果不装i价值最大,则取前者;如果装物品i价值最大,则取后者
  3. 初始化:
    • 由递推公式可以看出dp[i][j]取决于左面和和上面的最大值
    • 当背包容量为0时,最大价值都为0:
    for(int row = 0; row < M; ++row) {
        dp[row][0] = 0;
    }
    
    • 当背包只装物品0时,最大价值取决去背包是否能装下物品0
    for(int col = weights[0]; col <= N; ++col) {
        dp[0][col] = value[0];
    }
    
  4. 遍历顺序:由于dp[i][j]取决于dp[i-1][j]和dp[i-1][j-weight[i]] + value[i]所以先遍历背包和先遍历物品都可以
  5. 打印dp数组
#include <iostream>
#include <vector>
using namespace std;


int main() {
    
    int M, N;
    cin >> M >> N;
    // dp[i][j] 表示:下标为[0-i]的物品任取,放进容量为j的背包,价值最大是多少
    vector<vector<int>> dp(M, vector<int>(N+1, 0));
    vector<int> weights;
    vector<int> value(M);
    for(int i = 0; i < M; ++i) {
        int weight;
        cin >> weight;
        weights.push_back(weight);
    }

    for(int i = 0; i < M; ++i) {
        cin >> value[i];
    }
    
    
    // 递推公式dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i]] + value[i])
    
    // 初始化
    for(int row = 0; row < M; ++row) {
        dp[row][0] = 0;
    }
    for(int col = weights[0]; col <= N; ++col) {
        dp[0][col] = value[0];
    }
    
    for(int cap = 1; cap <= N; ++cap) {
        for(int item = 1; item <M; ++item) {
            if(cap < weights[item]) dp[item][cap] = dp[item-1][cap];
            else dp[item][cap] = max(dp[item-1][cap], (dp[item-1][cap-weights[item]] + value[item]));
        }
    }

    
    cout << dp[M-1][N];    

    return 0;
}

滚动数组

文章讲解 视频讲解
动态规划五部曲:

  1. dp[j]:容量为j的背包所能装物品的最大价值
  2. 递推公式:dp[j] = max(dp[j], dp[j-weights[i] + value[i])
  3. 初始化:由递推公式可知,dp数组每一次更新都依赖于本身的值,所以本身的值在初始化时不应过大,使得dp数组无法更新,所以初始化为0即可
  4. 遍历顺序:先遍历物品后遍历背包,且背包必须倒序遍历;、
    • 背包为什么要倒序遍历:
      递推公式dp[j-weight[i]] + value[i],由于j >= j - weight[i] 所以背包从小到大遍历dp[j - weighs[i]]这个值已经填过了,会影响到dp[j];试想dp[2] = dp[2-1] + value[1] 相当于将两个物品1装入了背包
    • 为什么先遍历物品:
      dp[j] = dp[j-weights[i] + value[i], 首先遍历最大容量的背包,此时并没有物品已经装入,是一个空的背包,所以j-weights[i] <= j的值全是0,这样相当于将所有物品放进取试一下装哪一个价值最大,结束后,背包将只装有一个物品
  5. 打印dp数组
#include <iostream>
#include <vector>
using namespace std;


int main() {
    
    int M, N;
    cin >> M >> N;
    
    vector<int> weights(M);
    vector<int> value(M);
    
    for(int i = 0; i < M; ++i) cin >> weights[i];
    for(int i = 0; i < M; ++i) cin >> value[i];
    
    
    // dp[j]: 容量为j时,dp数组的最大价值
    vector<int> dp(N + 1);
    
    // 递推公式dp[j] = max(dp[j], dp[j-weights[i]] + value[i])
    
    // 初始化
    for(int j = 0; j < N; ++j) {
        dp[j] = 0;
    }
    
    // 遍历顺序
    for(int i = 0; i < M; ++i) {
        for(int j = N; j >=0; --j) {
            if(j < weights[i]) continue;
            else dp[j] = max(dp[j], dp[j-weights[i]] + value[i]);
        }
    }
    // for(int i = 0; i < N; ++i) cout << dp[i];
    
    cout << dp[N];

    return 0;
}

416.分割等和子集

题目链接 文章讲解 视频讲解

问题抽象:物品i的重量nums[i], 价值也是nums[i]

  • dp[j]: 当前子集的和
  • 递推公式:p[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
  • 初始化:默认初始化为0
  • 遍历顺序:先遍历物品,再遍历背包;背包倒序遍历
  • 打印dp数组
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int target = 0;
        for(int val : nums) target += val;
        if(target % 2 == 1) return false;
        target = target >> 1;
        
        vector<int> dp(target + 1, 0);

        for(int i = 0; i < nums.size(); ++i) {
            for(int j = target; j >= 0; --j) {
                if(j < nums[i]) continue;
                else dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
            }
        }
        return dp[target] == target;
    }
};

标签:遍历,int,训练营,随想录,value,第四十一,背包,weights,dp
From: https://www.cnblogs.com/cscpp/p/18254150

相关文章

  • 代码随想录第四十一天 | 59.斐波那契数列,70.爬楼梯,71.使用最小花费爬楼梯
     59.斐波那契数列看完想法:虽然是最简单的动态规划问题,但还是要按照五部曲来分析intfib(intn){if(n<=1)returnn;vector<int>dp(n+1);//用n+1的原因是,定义数组时这个意思是数组的长度,n+1的话最后一个就是dp[n]dp[0]=0;......
  • 代码随想录算法训练营第38天|● 理论基础 ● 509. 斐波那契数● 70. 爬楼梯 ● 746.
    动态规划理论基础动态规划,英文:DynamicProgramming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的,动态规划做题步骤确定dp数组(dptable)以及......
  • 代码随想录第10天 | 栈与队列part01
    题目:232.用栈实现队列思路:1.使用双栈,一个作为输入,一个作为输出代码:classMyQueue{private:stack<int>A,B;public:MyQueue(){}voidpush(intx){A.push(x);}intpop(){//删除A栈底元素并返回元素intresult=this->p......
  • 代码随想录第11天 | ●字符串总结 ●双指针回顾
    字符串总结字符串是若干字符组成的有限序列,也叫字符数组。C语言中,把字符存入数组,以结束符'\0'为结束标志,'\0'可作为判断依据c++中,提供string类,string类提供各种接口,其中size()可作为结束判断标志。vector<char>和string相差不大,string类提供处理字符串的接口更多字符串类......
  • 代码随想录算法训练营第15天 |
    代码随想录算法训练营第15天翻转二叉树https://leetcode.cn/problems/invert-binary-tree/description/翻转二叉树代码随想录https://programmercarl.com/0226.翻转二叉树.html对称二叉树题https://leetcode.cn/problems/symmetric-tree/对称二叉树代码随想录https://pro......
  • 代码随想录算法训练营第39天 | 62.不同路径 、63. 不同路径 II
    今天开始逐渐有dp的感觉了,前两题不同路径,可以好好研究一下,适合进阶详细布置62.不同路径本题大家掌握动态规划的方法就可以。数论方法有点非主流,很难想到。https://programmercarl.com/0062.不同路径.html视频讲解:https://www.bilibili.com/video/BV1ve4y1x7Eu/***@p......
  • 代码随想录算法训练营第36期 last day
    最后一次更新,之后去复习专业课和简历583两个字符串的删除操作自己做出来了:Code:class Solution {public://找到公共子序列的最大长度dp 最小步数=串1.size-dp+串2.size-dp    int minDistance(string word1, string word2) {        vector<vector<int......
  • 代码随想录刷题记录(7)| 字符串(344.反转字符串,541. 反转字符串II,卡码网:54.替换数字)
    目录(一)反转字符串1.题目描述2.思路3.解题过程(二)反转字符串Ⅱ1.题目描述2.思路3.解题过程(三)替换数字1.题目描述2.思路3.解题过程(一)反转字符串344.反转字符串-力扣(LeetCode)1.题目描述        编写一个函数,其作用是将输入的字符串反转过......
  • 代码随想录刷题记录(8)| 字符串(151.反转字符串里的单词,卡码网:55.右旋转字符串,28. 找出字
    目录(四)反转字符串里的单词1. 题目描述2.思路3.解题过程(1)使用额外空间存储(2)原地反转 (五)右旋转字符串1.题目描述2.思路3.解题过程 (六)找出字符串中第一个匹配项的下标1.题目描述2.思路3.解题思路(七)重复的子字符串1.题目描述2.思路3.解题过程(八)......
  • 代码随想录算法训练营第六十天 | 647. 回文子串、516.最长回文子序列
    647.回文子串文字讲解:代码随想录视频讲解:动态规划,字符串性质决定了DP数组的定义|LeetCode:647.回文子串_哔哩哔哩_bilibili解题思路1.dp[i][j]     [i,j]子串是否是回文的      是则返回true,不是则返回false2.递推公式if(s[i]==s[j])   ......