Processing math: 100%
筷子与饺子
Published on 2019-11-11 04:08 in 暂未分类 with 筷子与饺子

416. 分割等和子集

416 分割等和子集

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

注意:

每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:

输入: [1, 5, 11, 5]

输出: true

解释: 数组可以分割成 [1, 5, 5] 和 [11].

示例 2:

输入: [1, 2, 3, 5]

输出: false

解释: 数组不能分割成两个元素和相等的子集.

思路


判断是否有等和子集,对数组求和,那么问题转化为,在数组是否有一个序列的和等于数组和的一半
问题变成了一个01背包问题;
利用动态规划来求解,普通解法是建立一个二维数组

dp[i][j] 表示从0i之间是否存在一种方案使得和为j 初始是 0即false;

  • 解决边界问题

dp[i][0]=true : 因为当和为0的时候,数组下标0i之间存在一种方案使得 和为0 (什么元素都不选)
dp[0][nums[0]]=true : 因为当和为nums[0]的时候,选择nums[0]为方案

  • 解决递推问题

if(j>=nums[i]) 那么nums[i] 可以不被选取,也可以被选取。只需要将两者情况取或(只要有一个为true 则dp[i][j]为true):
dp[i][j]=dp[i1][j]|dp[i][jnums[i]];

if(j<nums[i]) 那么该元素不被选取:
dp[i][j]=dp[i1][j];

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = accumulate(nums.begin(),nums.end(),0);
        int MaxNum = *max_element(nums.begin(),nums.end());
        //判断sum是否为奇数,按位与 最后一位是0 则为偶数,为1 则为奇数
        if(sum & 1) return false;
        int target = sum/2;  //转化为 0 1 背包问题,即判断数组中是否存在和为target 的序列

        if(MaxNum > target) return false;

        //定义动态规划数组 dp[i][j] 表示从0到i之间 有没有和为j的序列
        vector<vector<int> > dp(n,vector<int> (target+1,0));

        for(int i=0;i<n;i++)
        {
            dp[i][0] = true;
        }

        dp[0][nums[0]] = true;

        for(int i =1;i<n;i++)
        {
            int num = nums[i];
            for(int j=1;j<=target ;j++)
            {
                if(j>=num)
                {
                    dp[i][j] = dp[i-1][j] | dp[i-1][j-num];
                }
                else
                {
                    dp[i][j] = dp[i-1][j];
                }
            }

             if(dp[i][target]) return true;
        }

        return dp[n-1][target];
    }
};

优化空间


把dp二维数组优化为一维的,这是因为每一次状态转移,dp[i][j]的值只与上一行数组有关。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = accumulate(nums.begin(),nums.end(),0);
        int MaxNum = *max_element(nums.begin(),nums.end());
        //判断sum是否为奇数,按位与 最后一位是0 则为偶数,为1 则为奇数
        if(sum & 1) return false;
        int target = sum/2;  //转化为 0 1 背包问题,即判断数组中是否存在和为target 的序列

        if(MaxNum > target) return false;

        vector<int> dp(target+1,0);
        dp[0] = true;
        if(nums[0] <= target)
            dp[nums[0]] = true;

        for(int i=1;i<n;i++)
        {
            for(int j = target;nums[i] <= j; j--)
            {
                if(dp[target])
                {
                    return true;
                }
                dp[j] = dp[j] | dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

链接

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum

posted @   筷子与饺子  阅读(133)  评论(0编辑  收藏  举报
编辑推荐:
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
阅读排行:
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· 提示词工程师自白:我如何用一个技巧解放自己的生产力
· 一文搞懂MCP协议与Function Call的区别
· 如何不购买域名在云服务器上搭建HTTPS服务
点击右上角即可分享
微信分享提示