416.分割等和子集·背包问题·动态规划

链接:https://leetcode.cn/problems/partition-equal-subset-sum/solution/by-xun-ge-v-z0hr/
来源:力扣(LeetCode)

题目

示例

思路

解题思路
题目需要分割非空数组 nums 为两个数组,这个数组分割使得两个子集的元素和相等。
可以将题目转换一下为,将nums数组求和之后,除半,再在数组nums中是否可以寻找任意个元素累加和相等,元素唯一不重复选择

其实将题目转换之后和 零钱兑换 题目思路就差不多了。都是背包问题
相当于现在给我们一个容量有限背包和一堆东西,我们需要将这些东西装到背包里面,现在只需要判断能不能正好装满,我不关心里面装什么,装多少。
所以我们申请二维dp[i][j]数组,记录我们这个背包装了什么还能装什么,装没有装满,遇见东西时就有两个状态

  1. 没有装:dp[i][j] = dp[i-1][j],没有装我继承上一次背包状态
  2. 装了:
  • 正好装满dp[i][j] = true
  • 装了但是还没有装满:dp[i][j] = dp[i-1][j-nums[i]],东西我装了,判断一下我装了这个东西还有没有东西和我一起凑满

比如现在背包大小为5,有东西1,2,3,4(与题目无关),dp[i][j]表示背包大为j时,选择了东西i,当前背包是满(true),还是未满(false),所以dp[1][2]为true,表示我选择了东西2并且背包大小为2时,我背包满了,因此dp[2][3]=true,那么dp[2][5] = dp[2][3] + dp[1][2] = true,我背包大小为5,可以选择装东西3和东西2,当背包大小不能装下我的东西时继承i-1背包状态,因为我可以选择不装当前东西,那么背包也就没有变动,以此类推,最后返回dp[][max_j] 是否满
空间压缩
从上面可以看出来,我们并不关心背包里面装了什么地方,那么是不是没必要申请记录装东西的大小,当然是可以的,我们只关心背包能不能装满,因此我们申请dp[n]记录背包大小为n时能不能装满,但是在装东西的时候就需要从大背包开始装,因为这样可以保留上一次背包状态

总和必须为偶数,奇数/2 存在.5我们不管怎么找都不能在整数数组中找到小数

代码

空间未优化

**二维空间未优化**

bool canPartition(int* nums, int numsSize){
    int sum = 0;
    for(int i = 0; i < numsSize; i++)//统计和
    {
        sum += nums[i];
    }
    if(sum % 2)//是否为偶数,奇数/2 存在.5我们不管怎么找都不能在整数数组中找到小数
    {
        return false;
    }
    int dpLen = sum/2;
    bool dp[numsSize][dpLen + 1];//记录背包情况
    for(int i = 0; i <= dpLen; i++)//将第一个东西装入
    {
        if(nums[0] == i)
        {
            dp[0][i] = true;
        }
        dp[0][i] = false;
    }
    for(int i = 1; i < numsSize; i ++)//将其他的大小装入背包
    {
        for(int j = 1; j <= dpLen; j++)//更新背包情况
        {
            dp[i][j] = dp[i-1][j];
            if(nums[i] == j)//背包大小与东西大小一样
            {
                dp[i][j] = true;
            }
            else if(nums[i] < j)
            {
                if(dp[i - 1][j - nums[i]] == true)//装了当前东西背包有没有足够空间
                {
                    dp[i][j] = true;
                }
            }
        }
    }
    if(dp[numsSize-1][dpLen] == true)
    {
        return true;
    }
    return false;
}

空间压缩



bool canPartition(int* nums, int numsSize){
    int sum = 0;
    for(int i = 0; i < numsSize; i++)
    {
        sum += nums[i];
    }
    if(sum % 2)
    {
        return false;
    }
    int dpLen = sum/2;
    bool dp[dpLen + 1];
    for(int i = 0; i <= dpLen; i++)
    {
        if(nums[0] == i)
        {
            dp[i] = true;
        }
        dp[i] = false;
    }
    for(int i = 1; i < numsSize; i ++)
    {
        for(int j = dpLen; j >= 1; j--)
        {
            if(nums[i] == j)
            {
                dp[j] = true;
            }
            else if(nums[i] < j)
            {
                if(dp[j - nums[i]] == true)
                {
                    dp[j] = true;
                }
            }
        }
    }
    if(dp[dpLen] == true)
    {
        return true;
    }
    return false;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值