【开始刷LeetCode】416. 分割等和子集

一、绪

本篇文章主要用到动态规划的思想,并且这个问题和动态规划中经典的01背包问题类似,所以我们可以套用01背包的解法求解这个问题。

01背包的问题解法我也在另外一篇博客里进行了分析解释,详情可跳转链接: 【动态规划】01背包问题(装还是不装,老子不装啦。。。老子就是菜).

二、分割等和子集

链接: 416. 分割等和子集.

(一)题目描述

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

示例一:

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

示例二:

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

解题思路

    //1.求出该数组的总和,判断总和除与2是否为整数
        //若不为整数,则说明数组不能分割成两个元素和相等的子集,return false
    //2.将该问题以01背包问题的思想求解
    //(一个容量为1/2总和的背包能否刚好装满,若是能装满则说明数组能分割成两个元素和相等的子集)
        //定义一个行与列的个数为(1/2总和+1)的二维dp数组,0行0列初始化为0
        //从1开始遍历行列
            //1行1列时,判断容量为1的背包,能否装下重量为*(nums+1)的物体
                //若不能装下,此时的背包容量为这个物体前的最佳状态即可,即dp[1][1]=dp[0][1]
                //若能装下,判断上一个物体时容量为(当前背包的容量-该物体质量)的背包的最佳状态质量加上该物体质量能否比当前背包所包含的质量更趋近当前背包的最大容量
                    //若更趋近,则改变为上一个物体时容量为(当前背包的容量-该物体质量)的背包的最佳状态质量加上该物体质量,即dp[0][1-*(nums+1)]
                    //否则,则保持,即dp[1][1]=dp[0][1]
            //1行2列时,以此类推
        //直到dp数组遍历赋值结束
    //此时dp数组最后一行最后一列的数时最趋近于1/2总和,判断该数是否等于1/2总和
        //若等于,则说明数组能分割成两个元素和相等的子集,return true
        //否则,return false

(二)AC代码

1.一开始用了二维dp数组的解决代码

bool canPartition(int* nums, int numsSize){
    int sum=0;
    for(int i=0;i<numsSize;i++)
        sum+=*(nums+i);
    int half;
    if(sum%2!=0)
        return false;
    else
        half=sum/2;

    int dp[numsSize+1][half+1];

    for(int i=0;i<=numsSize;i++)
        dp[i][0]=0;
    for(int j=0;j<=half;j++)
        dp[0][j]=0;

    for(int i=1;i<=numsSize;i++){
        for(int j=1;j<=half;j++){
            if(j>=*(nums+i-1)){
                if(dp[i-1][j] <= dp[i-1][j-*(nums+i-1)]+*(nums+i-1))
                    dp[i][j] = dp[i-1][j-*(nums+i-1)]+*(nums+i-1);
                else
                    dp[i][j] = dp[i-1][j];
            }
            else
            dp[i][j] = dp[i-1][j];
        }
    }

    if(dp[numsSize][half] == half)
        return true;
    else
        return false;
}

2.进行空间优化之后,用的是一维dp数组的解决代码

bool canPartition(int* nums, int numsSize){
    int sum=0;
    for(int i=0;i<numsSize;i++)
        sum+=*(nums+i);
    int half;
    if(sum%2!=0)
        return false;
    else
        half=sum/2;

    int dp[half+1];

    for(int i=0;i<=half;i++)
        dp[i]=0;

    for(int i=0;i<numsSize;i++){
        for(int j=half;j>=1;j--){
            if(j>=*(nums+i)){
                if(dp[j] <= dp[j-*(nums+i)]+*(nums+i))
                    dp[j] = dp[j-*(nums+i)]+*(nums+i);
            }
        }
    }

    if(dp[half] == half)
        return true;
    else
        return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
题目描述: 给定一个只包含正整数的非空数组,是否可以将这个数组分成两个子集,使得两个子集的元素和相等。 示例: 输入:[1, 5, 11, 5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11]。 解题思路: 这是一道经典的 0-1 背包问题,可以使用动态规划或者回溯算法解决。 使用回溯算法,需要定义一个 backtrack 函数,该函数有三个参数: - 数组 nums; - 当前处理到的数组下标 index; - 当前已经选择的元素和 leftSum。 回溯过程中,如果 leftSum 等于数组元素和的一半,那么就可以直接返回 true。如果 leftSum 大于数组元素和的一半,那么就可以直接返回 false。如果 index 到达数组末尾,那么就可以直接返回 false。 否则,就对于当前元素,有选择和不选择两种情况。如果选择当前元素,那么 leftSum 就加上当前元素的值,index 就加 1。如果不选择当前元素,那么 leftSum 不变,index 也加 1。最终返回所有可能性的结果中是否有 true。 Java 代码实现: class Solution { public boolean canPartition(int[] nums) { int sum = 0; for (int num : nums) { sum += num; } if (sum % 2 != 0) { return false; } Arrays.sort(nums); return backtrack(nums, nums.length - 1, sum / 2); } private boolean backtrack(int[] nums, int index, int leftSum) { if (leftSum == 0) { return true; } if (leftSum < 0 || index < 0 || leftSum < nums[index]) { return false; } return backtrack(nums, index - 1, leftSum - nums[index]) || backtrack(nums, index - 1, leftSum); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

peng_YuJun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值