分割等和子集

LeetCode链接

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

 

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

 

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

这道题可以用回溯法暴力搜索来做,但是会超时,所以选择用dp来做

这题的思路和背包问题的思路类似,可以把背包问题的解决方法搬过来

背包问题需要求的是最大容量,这道题如果我们求出最大和为sum/2,那么就证明可以将数组分为两个元素和相等的子集

动规五部曲分析如下:

1.确定dp数组以及下标的含义

这里存储一维的数据dp[i],如果将背包问题的思路套到这道题里,那么就是前i的容量里和的最大值

这里容量其实我们可以用价值来代替,即容量和价值相同

2.确定递推公式

01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。

所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

3.dp数组如何初始化

如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。

这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了。

4.确定遍历顺序

两层循环遍历,外层i代表容量,内层j代表价值

5.举例推导dp数组

 

代码如下

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(10001,0);
        int sum = 0;
        for(int i = 0;i < n;i++)
        {
            sum += nums[i];
        }
        if (sum % 2 == 1)
        {
            return false;
        }
        int target = sum / 2;

        for(int i = 0;i < n;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;
    }
};

 

posted @ 2023-02-13 23:09  写在风中的信  阅读(28)  评论(0编辑  收藏  举报