代码随想录训练营第42天|416.分割等和子集

文章讨论了一种利用动态规划解决等和子集的问题,该问题最初尝试用回溯法解决但超时。通过转换为背包问题,采用二维和一维动态规划数组进行优化,减少了时间复杂度。当物品总价值的一半能被数组中的数值匹配时,表示可以等和分割,返回true;否则返回false。
摘要由CSDN通过智能技术生成

分割等和子集

对于这道题,我的第一想法是回溯,但是经过运行后,发现超时了,经过剪枝后还是超时了,所以大家不要尝试了,因为回溯的时间复杂度是2^n,不如直接用动态规划。
我们将这个问题视为背包问题,背包的大小无限大,物品的价值与大小均是这个nums对应的数值.物品的总价值为tars,如果tars不是2的倍数,说明一定不可以分,令tar=tars/2,即总容量的一半。
如果最后求得某一行背包内的价值等于了这些物品的价值,就说明背包内刚好可以装下价值为总价值一半的物品,也就是可以等和分割,也就是有解,返回true。

二维解法

我们将这个集合视为一个背包,因为,我们无需考虑背包总容量大于tar的情况,因此,我们初试化dp数组的行数为物品的数量,列数为tar+1,初始化,第一行的值,小于nums[0]的位置值为0,大于等于nums[0]的位置,值为nums[0]。
求背包问题,我们先对物品变量,再对价值遍历(二维数组对遍历顺序没有要求)。
对于dp[ii][jj]。我们令其的值dp[ii][jj] = max(dp[ii-1][jj],dp[ii-1][jj-nums[ii]]+nums[ii]),跟背包问题的递推式子相同。
但是需要考虑jj<nums[ii]的情况,因为此时是越界的,因此,我们直接copy上一层的元素即可。

代码
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int tar = 0;
        for(int ii =0;ii<nums.size();ii++)
        {
            tar +=nums[ii];
        }
        if(tar%2==1) return false;
        tar/=2;
        vector<vector<int>> dp(nums.size(),vector<int>(tar+1,0));

        for(int ii=nums[0];ii<dp[0].size();ii++)
            dp[0][ii] = nums[0];
        for(int ii =1;ii<nums.size();ii++)
        {
            for(int jj =0;jj<dp[0].size();jj++)
            {
                if(jj<=nums[ii])
                dp[ii][jj] = dp[ii-1][jj];
                else
                    dp[ii][jj] = max(dp[ii-1][jj],dp[ii-1][jj-nums[ii]]+nums[ii]);
            }
            if(dp[ii][tar]==tar) return true;
        }
            return false;
    }
};

一维解法

对于一维的解法,显然会在空间复杂度上优于二维解法,但是一维的解法只能从后向前遍历,因为,后面的元素需要上一层前面的值,如果从前向后遍历,遍历到一个元素时,前面的值可能已经被修改,此时就无法得到正确的值。

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int tar = 0;
        for(int ii =0;ii<nums.size();ii++)
        {
            tar +=nums[ii];
        }
        if(tar%2==1) return false;
        tar/=2;
        vector<int>dp(tar+1,0);
        for(int ii =nums[0];ii<dp.size();ii++)
            dp[ii] = nums[0];
        for(int ii =1;ii<nums.size();ii++)
        {
            for(int jj =dp.size()-1;jj>=nums[ii];jj--)
            dp[jj] = max(dp[jj],dp[jj-nums[ii]]+nums[ii]);
            if(dp[tar] == tar) return true;
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值