力扣hot100:416.分割等和子集(组合/动态规划/STL问题)

这篇具有很好参考价值的文章主要介绍了力扣hot100:416.分割等和子集(组合/动态规划/STL问题)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

力扣hot100:416.分割等和子集(组合/动态规划/STL问题),# 力扣,leetcode,算法,职场和发展

组合数问题

我们思考一下,如果要把数组分割成两个子集,并且两个子集的元素和相等,是否等价于在数组中寻找若干个数使之和等于所有数的一半?是的!

因此我们可以想到,两种方式:

①回溯的方式找到target,但是回溯是阶乘级别的算法,这里会超时。

②从前往后遍历,形象说明:定义n个集合dp[i],dp[i]表示前i个数的可能组合值,则有dp[i]={dp[i-1],dp[i-1]+i},dp[i-1]+i指的是i加上dp[i-1]中的所有元素得到的集合。同时由于长度最长200,数值最大为100,因此数的范围最大为1<=i<=20000,因此最多只有20000个不同的数。因此dp最大为2W个数!所以我们可以用集合实现,并且时间复杂度满足要求O(nums.length*nums.length*max(nums[i]))!

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(auto &i:nums) sum+=i;
        if(sum&1||nums.size()==1) return false;//只有偶数总和可以
        sum>>=1;
        unordered_set<int> Set;
        vector<int> temp;
        for(auto &i:nums){
            if(i==sum) return true;
            for(auto it=Set.cbegin();it!=Set.cend();++it){
                int cur=*it+i;
                if(cur==sum) return true;
                if(cur<sum) temp.emplace_back(cur);//>sum的没意义
            }
            for(auto & j:temp) Set.insert(j);
            temp.clear();
            Set.insert(i);
        }
        return false;
    }
};

遇到的问题:

        在循环遍历的过程中往容器中插入元素会导致容器迭代器end()和size(),时刻发生变化。与此同时,有的容器比如vector和string,往后面增加元素超过容量capacity可能会导致拷贝,从而整个迭代器失效。因此!在使用循环并且需要添加元素时,想使用迭代器需要额外注意!

力扣hot100:416.分割等和子集(组合/动态规划/STL问题),# 力扣,leetcode,算法,职场和发展

力扣hot100:416.分割等和子集(组合/动态规划/STL问题),# 力扣,leetcode,算法,职场和发展

比如本题是先将所需增加的放入到一个vector中的。

我们不能企图使用临时“指针”迭代器i=end(),然后遍历到it!=i,这是错误的!因为end()可能失效,而不是end()当时指向的位置。以下是C++ prime中特地提到的(这么小,但又特别灾难的坑被遇到了)

力扣hot100:416.分割等和子集(组合/动态规划/STL问题),# 力扣,leetcode,算法,职场和发展

int tmp=Set.size();
for(auto it=Set.cbegin();tmp!=0;++it,--tmp){ 
    int cur=*it+i;
    if(cur==sum) return true;
    temp.emplace_back(cur);
    Set.insert(cur);
}

这样做仍然是错误的,实际上我们在往非顺序容器中插入元素时,容器的数据结构会发生变化,因此导致++it可能并非按照原来的想法遍历,所以是错误的!

唯一正确且安全的方式是,如果需要在遍历的时候同时添加元素,最好不要使用迭代器!迭代器可以很好的遍历元素或者按迭代器范围赋值。但是边添加边遍历问题非常大。

因此对于无序容器只能使用迭代器的情况下,使用临时容器存储是非常必要的;对于顺序容器可以使用下标的情况下,使用下标更好。

动态规划

        这道题实际上和0-1背包问题很像,即从n个物品中拿出一部分使得其值刚好等于target。意识到这一点我们可以定义动态转移方程:

dp[i][k]=max(dp[i-1][k-nums[i]],dp[i-1][k]);

dp[0][nums[0]]=1;

//dp[i][k]表示拿前i个物品是否可以达到重量k。

        这实际上和方法一组合数问题是异曲同工的,方法一使用集合实现,那么如果方法一使用的是数组实现呢?那么数组中标记为1的实际上就是方法二的状态了!

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(auto &i:nums) sum+=i;
        if(sum&1||nums.size()==1) return false;
        sum>>=1;
        vector<vector<int>> dp(nums.size(),vector<int>(sum+1,0));//超过sum实际上就不用看了
        if(nums[0]<=sum)
            dp[0][nums[0]]=1;
        for(int i=1;i<nums.size();++i){
            for(int j=1;j<=sum;++j){
                dp[i][j]=dp[i-1][j];
                if(j-nums[i]>=0)
                    dp[i][j]=max(dp[i-1][j-nums[i]],dp[i-1][j]);
            }
            if(nums[i]<=sum)
                dp[i][nums[i]]=1;
        }
        return dp[nums.size()-1][sum];
    }
};

由于只用到前面的值,很显然我们可以降维优化。文章来源地址https://www.toymoban.com/news/detail-860549.html

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(auto &i:nums) sum+=i;
        if(sum&1||nums.size()==1) return false;
        sum>>=1;
        vector<int> dp(sum+1,0);//超过sum实际上就不用看了
        if(nums[0]<=sum)
            dp[nums[0]]=1;
        for(int i=1;i<nums.size();++i){
            for(int j=sum;j>=nums[i];--j){
                dp[j]=max(dp[j],dp[j-nums[i]]);
            }
        }
        return dp[sum];
    }
};

到了这里,关于力扣hot100:416.分割等和子集(组合/动态规划/STL问题)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Day42|动态规划part04: 01背包问题,你该了解这些!、滚动数组、416. 分割等和子集

    其他背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。 而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。 01 背包问题描述 有n件物品和一个最多能背重量为w 的背包

    2024年04月25日
    浏览(31)
  • 【十七】【动态规划】DP41 【模板】01背包、416. 分割等和子集、494. 目标和,三道题目深度解析

    动态规划就像是解决问题的一种策略,它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题,并将每个小问题的解保存起来。这样,当我们需要解决原始问题的时候,我们就可以直接利用已经计算好的小问题的解,而不需要重

    2024年02月03日
    浏览(32)
  • 代码随想录 Day35 动态规划04 01背包问题和完全背包问题 LeetCode T416 分割等和子集

    说到背包问题大家都会想到使用动规的方式来求解,那么为什么用动规呢, dp数组代表什么呢 ? 初始化是什么 , 遍历方式又是什么 ,这篇文章笔者将详细讲解背包问题的经典例题0-1背包问题和完全背包问题的解题方式,希望能帮助到大家 有人一提到背包问题就只会使用动态规划来

    2024年02月06日
    浏览(46)
  • 第九章 动态规划part04(● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集 )

    ● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集 https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-1.html 视频讲解:https://www.bilibili.com/video/BV1cg411g7Y6 1.确定dp数组以及下标的含义 i是物品,j是背包容量

    2024年01月16日
    浏览(37)
  • 力扣算法刷题Day42|动态规划:01背包问题 分割等和子集

    力扣题目:01背包问题(二维数组) 刷题时长:参考题解 解题方法:动态规划 + 二维dp数组 复杂度分析 时间 空间 问题总结 理解递推公式困难 本题收获 动规思路:两层for循环,第一层i遍历物品,第二层j枚举背包容量以内所有值 确定dp数组及下标的含义:dp[i][j] 表示从下标

    2024年02月13日
    浏览(41)
  • 动态规划(分割等和子集)

    题目难易:中等 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 示例 2: 输入: [1, 2

    2024年02月02日
    浏览(29)
  • LeetCode416. 分割等和子集

    一、题目 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 示例 2: 提示: 1 = nums.length = 200 1 = nums[i] = 100 二、题解 方法一:0-1背包二维数组 步骤1:理解题目以及01 背包问题 在 01 背包问题中,

    2024年02月10日
    浏览(37)
  • 【LeetCode】416.分割等和子集

    给你一个  只包含正整数  的  非空  数组  nums  。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 示例 2: 提示: 1 = nums.length = 200 1 = nums[i] = 100 实际上是求能否从背包里选取元素,使这些元素之和等于数组所有元素之和的一半。dp

    2024年02月11日
    浏览(29)
  • Leetcode 416 分割等和子集

    题意理解 :         给你一个  只包含正整数  的  非空  数组  nums  。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。         即将数组的元素分成两组,每组数值=sum(nums)/2         若能分成这样的两组,则返回true,否则返回false      

    2024年02月01日
    浏览(28)
  • 【算法与数据结构】416、LeetCode分割等和子集

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题可以抽象成一个01背包的问题,关于01背包可以看【算法与数据结构】算法与数据结构知识点。 本题只需要求出数组的累积和,然后和的一半就可以视为背包的最大重量,目标

    2024年01月19日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包