【Leetcode HOT100】分割等和子集 c++

题目描述:

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

给你一个 只包含正整数 的 非空 数组 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

c++代码:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n<2)return false;//若数组元素少于2,不可能分割出两个数组
        int sum = 0,max=nums[0];
        for(int i=0;i<n;i++){
            sum+=nums[i];
            if(nums[i]>max)max=nums[i];
        }
        if(sum%2!=0 || max>(sum/2))return false; //若数组元素总和不能平分或最大值超过一半,不可能分割出两个数组
        int dp[n][sum/2+1]; //dp[i][j]记录从第0个到到第i个数的总和为j的可能性,1位可能,0位不可能
        for(int i=0;i<n;i++){
            for(int j=0;j<=sum/2;j++)dp[i][j]=0;
        }
        for(int i=0;i<n;i++)dp[i][0]=1; //一个数都不要就可以让和等于0
        dp[0][nums[0]]=1; //初始化第一个可以直观计算的dp[i][j],dp[0][nums[0]]=1
        for(int i=1;i<n;i++){
            for(int j=1;j<=(sum/2);j++){
                if(nums[i]<=j){ //当前nums[i]小于目标和j,可以要nums[i],也可不要nums[i]
                    dp[i][j]=dp[i-1][j-nums[i]] | dp[i-1][j];
                }
                else if(j<nums[i]){ //nums[i]大于目标和,不能要nums[i]
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n-1][sum/2];
    }
};

问题为“使得两个数组加和结果相同”,这可以转化为在一个数组中找到几个数字,使其加和结果为数组总和的一半。若数组总和为奇数,或者最大的数字比总和的一半还大,那么不可能分割出两个加和结果相同的数组。

问题可以分解成每个子问题,每个子问题直接又相互不独立。使用动态规划

dp[i][j]表示从第0个到第i个数字,之和等于j的可能性,0表示不可能,1表示可能

对于每个数字nums[i],考虑nums[i]和j的大小关系,如果nums[i]下于目标和j,则一定不能选择nums[i],加和结果也没变,则关系式为dp[i][j] = dp[i-1][j]如果nums[i]大于等于目标和j,那么可以选择nums[i],也可以不选择nums[i],这两种可能只要有一种可以满足目标和,就说明到第i个位置j是可以满足的,则关系式为dp[i][j] = dp[i-1][j] | dp[i-1][j-nums[i]]

注意初始化,当j=0,只要不选择任何数字,则一定可以满足加和等于0,所以所有的dp[i][0]=0。此外,再初始化一下j=0时的取值,可以肯定的是dp[0][nums[0]]=1

两层for循环来计算所有的dp[i][j],最后dp[n-1][sum/2]即为问题的答案。
在这里插入图片描述
总结:

问题转化,“两个相等”类型的问题可以转化为“总和的一半”,转化为常规的动态规划问题。用dp[i][j]来逐步计算。注意初始化dpi=0的时候,j=0的时候,dp[i][j]的值为初始值)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值