力扣416. 分割等和子集

题目来源(力扣):

https://leetcode.cn/problems/partition-equal-subset-sum/description/

题目描述:

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

基本思路:

0-1背包问题。所有数的总和为sum,设dp[i][j]表示对于前i个数是否可以组合出数字j

递推公式
j<0时,
dp[i][j]=dp[i-1][j];
j>0时,
dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]);

初始化:
初始化dp数组的第一行和第一列,
第一行,即对于第0个数nums[0],dp[0][nums[0]]=1;
第一列,即无论i为多少,当总和j为0时,一定有dp[i][0]=1;

代码实现:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        int len = nums.size();
        for (int i : nums)
            sum += i;
        // 0.特判,如果总和不是偶数,直接退出
        if (sum % 2)
            return 0;

        // 1.确定dp数组及其含义 dp[i][j]表示前i个数是否能组合成数字j
        bool dp[203][20003] = {0};

        // 2.确定递推关系
        // dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]&&1);

        // 3.初始化 第一列,dp[i][0]=1;第一行,dp[0][nums[0]]=1
        for (int i = 0; i < len; i++)
            dp[i][0] = 1;
        dp[0][nums[0]] = 1;

        // 4.确定遍历顺序 从上往下,从左往右
        for (int i = 1; i < len; i++) {
            for (int j = 0; j <= sum / 2; j++) {
                if (j < nums[i])
                    dp[i][j] = dp[i - 1][j];
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]]);
            }
        }

        // for (int i = 1; i < len; i++) {  //或者从上往下,从右往左 也可以
        //     for (int j = sum / 2; j >= 0; j--) {
        //         if (j < nums[i])
        //             dp[i][j] = dp[i - 1][j];
        //         else
        //             dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]]);
        //     }
        // }

        // for (int j = 0; j <= sum / 2; j++) {  //或者从左往右,从上往下 也可以
        //     for (int i = 1; i < len; i++) {
        //         if (j < nums[i])
        //             dp[i][j] = dp[i - 1][j];
        //         else
        //             dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]]);
        //     }
        // }



        // 5.打印部分数组以确定是否符合预期
        //  for(int j=0;j<=sum/2;j++)
        //      cout<<dp[len-1][j]<<" ";
        //  cout<<endl;

        return dp[len - 1][sum / 2];
    }
};

时间复杂度

O(n*sum) 其中n为数的个数,sum为所有数的总和

补充-一维dp

可以将二维dp压缩为1维

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        int len = nums.size();
        for (int i : nums)
            sum += i;
        // 0.特判,如果总和不是偶数,直接退出
        if (sum % 2)
            return 0;

        // 1.确定dp数组及其含义 dp[i][j]表示前i个数是否能组合成数字j
        bool dp[20003] = {0};

        // 2.确定递推关系
        // dp[j]=max(dp[j],dp[j-nums[i]]&&1);

        // 3.初始化 第一行,dp[0]=dp[nums[0]]=1
        dp[0]=1;
        dp[nums[0]] = 1;

        // 4.确定遍历顺序 从上往下,从右往左 
        for (int i = 1; i < len; i++) {
            for (int j = sum/2; j >= nums[i]; j--) {
                dp[j]=max(dp[j],dp[j-nums[i]]&&1);
            }
        }

        // 5.打印部分数组以确定是否符合预期
        //  for(int j=0;j<=sum/2;j++)
        //      cout<<dp[j]<<" ";
        //  cout<<endl;

        return dp[sum / 2];
    }
};
posted @ 2024-12-11 10:25  HB_Computer  阅读(25)  评论(0)    收藏  举报