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

893a7603c38744d3a8d19eb2d79e5588.png

组合数问题

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

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

①回溯的方式找到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可能会导致拷贝,从而整个迭代器失效。因此!在使用循环并且需要添加元素时,想使用迭代器需要额外注意!

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

我们不能企图使用临时“指针”迭代器i=end(),然后遍历到it!=i,这是错误的!因为i一直指向的仍然是end(),而不是end()当时指向的位置,换句话说end()不是一个具体的位置,而是一个抽象的位置。

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];
    }
};

由于只用到前面的值,很显然我们可以降维优化。

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];
    }
};

 

 

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 05:26:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 05:26:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 05:26:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 05:26:03       20 阅读

热门阅读

  1. 封装promise请求方式

    2024-03-17 05:26:03       21 阅读
  2. OLLAMA:如何像云端一样运行本地大语言模型

    2024-03-17 05:26:03       22 阅读
  3. alibaba cloud linux 3 安装 psql 16

    2024-03-17 05:26:03       23 阅读
  4. Python强大的库和框架——TensorFlow

    2024-03-17 05:26:03       19 阅读
  5. springBoot整合Redis(四、整合redis 实现分布式锁)

    2024-03-17 05:26:03       20 阅读
  6. lammps从NVT或者NPT切换到NVE时温度持续上升

    2024-03-17 05:26:03       23 阅读
  7. Python列表详解

    2024-03-17 05:26:03       20 阅读
  8. 【C语言】打印1-100之间所有3的倍数的数字

    2024-03-17 05:26:03       20 阅读