【力扣】416.分割等和子集

AC截图

题目

思路

dp[i][j]的定义

  • i:表示当前考虑的是数组中前i个元素(从0开始计数)。也就是说,i代表了我们当前可以使用的元素范围是从第一个元素到第i个元素。

  • j:表示我们尝试达到的目标和。这个值从0target(其中target是数组总和的一半)。

因此,dp[i][j]是一个布尔值,表示是否可以通过选择数组中前i+1个元素中的某些元素,使得这些元素的和恰好等于j

初始化

  • 对于所有的i,设置dp[i][0] = true,这是因为对于任何一组元素,都存在一种情况即不选取任何元素,此时元素的和为0

  • 对于dp[0][nums[0]],如果nums[0] <= target,则设置dp[0][nums[0]] = true,因为第一个元素本身就能构成和为nums[0]的子集。

状态转移方程

对于每个元素nums[i],我们需要更新dp数组以反映包含或不包含当前元素的所有可能性。具体的状态转移方程如下:

  • 如果目标和j大于等于当前元素num(即nums[i]),则有两种情况:

    • 不选择当前元素:dp[i][j] = dp[i-1][j]

    • 选择当前元素:dp[i][j] = dp[i-1][j-num]。这意味着如果我们选择了当前元素,则需要检查剩余部分(即j-num)是否可以通过前i-1个元素来实现。

    因此,状态转移方程为:dp[i][j] = dp[i-1][j] | dp[i-1][j-num]

  • 如果j < num,则不能选择当前元素,只能依赖不选择当前元素的情况:dp[i][j] = dp[i-1][j]

最终结果

最终的结果是dp[n-1][target],其中n是数组的长度,target是数组总和的一半。如果dp[n-1][target]true,则表示可以找到一个子集,其元素之和等于target,从而可以将原数组分割成两个和相等的子集。

实例

以nums = [1,5,11,5]为例

nums[i]/j01234567891011
nums[0] = 1TTFFFFFFFFFF
nums[1] = 5TTFFFTTFFFFF
nums[2] = 11TTFFFFFFFFFT
nums[3] = 5TTFFFTTFFFFT

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if(nums.size()<2) return false;

        int sum = 0;
        int mx = nums[0];
        for(int i=0;i<nums.size();i++){
            sum += nums[i];
            if(mx<nums[i]){
                mx = nums[i];
            }
        }

        if(sum%2!=0) return false;
        int target = sum/2;
        if(mx>target) return false;

        int n = nums.size();
        vector<vector<bool>> dp(n,vector<bool>(target+1));

        // 对于前i个正整数,j=0代表一个正整数都不取,是可以做到的
        for(int i=0;i<n;i++){
            dp[i][0] = true;
        }
        // 对于第一个正整数,如果放下,就是dp[0][nums[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(num<=j){
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-num];
                }else{
                 //当前正整数大于目标值,不能放入
                    dp[i][j] = dp[i-1][j];   
                }
            }
        }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值