代码随想录算法训练营第四十一天|卡码网46 携带研究材料、 LeetCode416 分割等和子集

文章介绍了如何使用动态规划方法解决0-1背包问题,通过定义dp数组表示在给定空间限制下选择物品的最大价值,并详细解释了递推公式和初始化过程。同时提及了一个应用——分割等和子集的问题,将该问题转化为01背包问题进行求解。
摘要由CSDN通过智能技术生成

46.携带研究材料

思路:0-1背包问题。确定dp数组及其下标的含义。dp[i][j]表示向空间大小为j的背包里放0~i个物品的最大价值。递推公式dp[i][j] = max(dp[i-1][j],dp[i-1][j-space[i]]+value[i]),有两种情况,不放i进背包,放i进背包,放i进背包的话,背包至少为i空余处space[i]的位置。初始化dp数组,dp[i][0]=0,因为背包容量为0时,什么都放不进去,价值为0。dp[0][i] = value[i] i>=space[i]。当背包容量大于第一个物品的容量时价值为第一个物品的价值。当前位置的价值只与上左方向位置的价值有关,因此遍历顺序为从左到右从上到下。打印dp数组,可以用于debug。

#include<iostream>
using namespace std;
#include<vector>
int maxvalue(vector<int>& space,vector<int>& value,int N,int M)
{
    //确定dp数组及其下标的含义 dp[i][j]代表0~i号物品放入空间j的最大价值
    //递推公式 dp[i][j] = max( dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
    //初始化dp数组 dp[i][0] = 0; dp[0][j] = value[0]; j>=space[0];
    //遍历顺序 从左到右 从上到下 当前值取决于左上值
    //打印dp数组,用于debug
    vector<vector<int>> dp(M,vector<int>(N+1,0));
    for(int j = space[0];j<=N;j++)
    {
        dp[0][j] = value[0];
    }
    for(int i = 1;i<M;i++)
    {
        for(int j = 0;j<=N;j++)
        {
            if(j<space[i])
            {
                dp[i][j] = dp[i-1][j];
            }
            else
            {
                dp[i][j] = max( dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
            }
        }
    }
    return dp[M-1][N];
}

int main()
{
    int M;
    int N;
    cin>>M;
    cin>>N;
    vector<int> space(M);
    vector<int> value(M);
    for(int i =0;i<M;i++)
    {
        cin>>space[i];
    }
    for(int i = 0;i<M;i++)
    {
        cin>>value[i];
    }
    cout<<maxvalue(space,value,N,M);
    
    system("pause");
    return 0;
}

思路:滚动数组解法,用一维dp数组,但value的循环需要倒序,因为要保证每个物品只用一次。

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

int bag(int M,int N,vector<int> &space,vector<int> &value)
{
    //确定dp数组及其下标含义 dp[j]表示背包j容量可以携带的最大价值
    //递推函数 dp[j] = max(dp[j],dp[j-space[i]+value[i]);
    //初始化dp数组 vector<int>dp(M,0); 假设价值最小为0
    vector<int> dp(N+1,0);
    for(int i = 0;i<M;i++)
    {
        for(int j = N;j>=space[i];j--)
        {
            dp[j] = max(dp[j],dp[j-space[i]]+value[i]);
        }
    }
    return dp[N];
}

int main()
{
    int M;
    int N;
    cin>>M;
    cin>>N;
    vector<int> space(M,0);
    vector<int> value(M,0);
    for(int i=0;i<M;i++)
    {
        cin>>space[i];
    }
    for(int j = 0;j<M;j++)
    {
        cin>>value[j];
    }
    
    cout<< bag(M,N,space,value);
    
    
    system("pause");
    return 0;
}

416.分割等和子集

思路:如果子集能分为两个子集,两个子集的元素和相等,则返回true。 可以考虑成01背包问题,集合中的每个元素的值即代表空间也代表价值。确定dp数组及其下标含义,dp[j]代表空间为j的背包的最大价值。递推公式,dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]); 初始化dp数组,价值为非零整数,初始化为0.遍历顺序,从后往前,因为每个元素只能用一次,打印dp数组,用于debug。当dp[target] = target。说明有子集满足和为sum/2。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        //利用动态规划求解,考虑成一个01背包问题
        //确定dp数组及其下标含义 dp[j] 为空间为j的背包所能装的最大价值
        //递推公式 dp[j] = max( dp[j], dp[j-space[i]]+vaule[i])
        //初始化dp数组 最小价值为一个不为负的整数
        //打印dp数组,用于debug
        int sum=0;
        for(int i =0;i<nums.size();i++)
        {
            sum+=nums[i];
        }
        if(sum%2==1)
        {
            return false;
        }
        else
        {
            int target = sum/2;
            vector<int> dp(target+1);
            for(int i =0;i<nums.size();i++)
            {
                for(int j = target;j>=nums[i];j--)
                {
                    dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
                }
            }
            if(dp[target]==target)
            {
                return true;
            }
        }
        return false;
    }
};

收获

一维dp数组倒序。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值