代码随想录Day42 | 背包问题理论 416. 分割等和子集

二维dp解决背包问题

文档讲解:代码随想录
视频讲解: 带你学透0-1背包问题
状态

利用五部曲来分析二维dp是如何解决背包问题的。

  1. dp[i][j]的含义
    i表示当前第i个物品,j表示背包的容量,dp[i][j]表示从0~i个物品中选取并放入容量为j的背包的价值最大和
  2. 递推公式
    主要有两种情况:
    • 放入第i个物品超重,即当前的容量小于i的重量,那么dp[i][j] = dp[i-1][j]
    • 放入第i个物品不超重,dp[i][] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
  3. 初始化
    • 考虑dp[i][0]:表示容量为0的背包的最大价值为0
    • 考虑dp[0][j]: 表示容量为0~j的背包,放入0号物品应该有的价值
    • 其余位置初始化为0
  4. 遍历顺序
    先背包再物品,或者先物品再背包都可以。
    主要是都要从左上角向dp[i][j]计算
  5. 打印dp数组

一维dp求解背包问题

文档讲解:代码随想录
视频讲解:
状态

本质是对递推公式的考虑进行修改,对于原始的二维dp,dp[i-1][j]来表示上一层的最大价值,上一层可以重复利用,那么直接拷贝到当前层就可以,这样就会少一阶数组。
对于1维的dp,五部曲分析

  1. dp[j]
    j表示背包容量,dp[j]就表示背包在当前容量下的最大价值
  2. 递推公式
    j容量的背包还是考虑是否可以继续装下重量为weight[i]的物品
    dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
  3. 初始化
    对于dp[0],那就是容量为0的背包价值就是0,
    对于dp[j],如果物品价值都是正整数,那么应当初始化为0
  4. 遍历顺序
    双层遍历,先物品再背包,背包必须倒序遍历。主要是为了避免重复计算物品数量。
    本质都是右下角的值是通过左上角得到的。但对于一维dp,相当于是右边的值需要左边值来更新。
  5. 打印dp数组

46.携带研究材料

文档讲解:代码随想录
视频讲解:
状态

二维dp

#include <iostream>
#include <vector>


int main()
{
    int M,N;
    std::cin >> M >> N;
    
    //输入材料所占空间
    std::vector<int> weight(M,0);
    for(int i = 0;i<M;i++)
    {
        std::cin >> weight[i];
    }
    //输入材料价值
    std::vector<int> value(M,0);
    for(int i = 0;i<M;i++)
    {
        std::cin >> value[i];
    }
    
    //创建二维dp
    std::vector<std::vector<int>> dp(M+1,std::vector<int>(N+1,0));
    //初始化dp
    for(int j = 0;j<N+1;j++)
    {
        if(j<weight[0])
        {
            dp[0][j] = 0;
        }
        else
        {
            dp[0][j] = value[0];
        }
    }
    
    //遍历
    for(int i =1;i<M+1;i++)
    {
        for(int j = 0;j<N+1;j++)
        {
            if(j<weight[i])
            {
                dp[i][j] = dp[i-1][j];
            }
            else
            {
                dp[i][j] = std::max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
            }
        }
    }
    
    std::cout << dp[M][N];
}

一维dp

#include <iostream>
#include <vector>


int main()
{
    int M,N;
    std::cin >> M >> N;
    
    //输入材料所占空间
    std::vector<int> weight(M,0);
    for(int i = 0;i<M;i++)
    {
        std::cin >> weight[i];
    }
    //输入材料价值
    std::vector<int> value(M,0);
    for(int i = 0;i<M;i++)
    {
        std::cin >> value[i];
    }
    
    //创建一维dp
    std::vector<int> dp(N+1,0);
    //初始化  全部为0
    
    //遍历
    for(int i = 0;i<M+1;i++)
    {
        for(int j = N;j>=weight[i];j--)
        {
            dp[j] = std::max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    
    std::cout<<dp[N];
}

416.分割等和子集

文档讲解:代码随想录
视频讲解: 动态规划之背包问题,这个包能装满吗?| LeetCode:416.分割等和子集
状态

如何转换为背包问题

  • 本题求解就是在数组中搜索元素(物品)然后它们的和为sum/2(价值)
  • 重量如何确定–重量就是价值,当重量和价值相等时表示能够搜索到。即背包正好装满

五部曲

  1. dp[j]
    j表示当前重量,dp[j]表示当前重量下的价值和。
    j容量的背包还是考虑是否可以继续装下重量为weight[i]的物品
  2. 递推关系
    dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
  3. 初始化
    对于dp[0],那就是容量为0的背包价值就是0
  4. 遍历顺序
    先物品再背包,背包倒序
  5. 打印dp
    在这里插入图片描述
//求和为sum/2的元素和
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 == 1) return false;
        int target = sum/2;

        //dp数组
        vector<int> dp(target+1,0);
        //初始化都为0, 全为正整数
        for(int i = 0;i<nums.size();i++)
        {
            for(int j = target;j>=nums[i];j--)
            {
                //nums[i]为重量 也为价值
                dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[target] == target) return true;
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值