Leetcode 416. Partition Equal Subset Sum

本题的题干很简洁:判断数组是否能分为和相同的两个部分。

思路:受限判断是否有可分的可能,如果整个数组的和为奇数,则一定不可能。然后将问题转化为判断是否能用数组中n-1个数得到sum/2。


方法一 动态规划

数组长为n,和为sum/2,使用n行sum/2+1的数组dp,dp[i][j]表示使用数组0到i的元素,可以得到结果 j 。

数组的的更新顺序与行列顺序一致。即对每一个数组元素,判断1到j是否能被组合得到。

更新方程:

若 j>nums[i] ,则 dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]]。

否则, dp[i][j] = dp[i-1][j]。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        int maxNum=-1;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
            maxNum=max(maxNum, nums[i]);
        }

        // 若和为奇数或者最大元素大于和的一般,返回false
        if(sum%2==1 || maxNum>sum/2)
            return false;
        
        // dp[i][j]的含义:nums从0到i的元素可以组合得到j
        vector<vector<bool>> dp(nums.size(), vector<bool>(sum/2 +1, false));

        // 判断目标数(j)为0,或者只取第一个数(nums[0])的情况
        for(int i=0;i<nums.size();i++){
            dp[i][0]=true;
        }
        if(nums[0]<=sum/2)
            dp[0][nums[0]]=true;

        for(int i=1;i<nums.size();i++){
            for(int j=1;j<=sum/2;j++){
                if(j>nums[i]) dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
                else dp[i][j] = dp[i-1][j];
            }
        }

        return dp[nums.size()-1][sum/2];
    }
};

方法二 动态规划+循环数组

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(int& n:nums)
            sum+=n;
        
        if(sum%2==1)
            return false;
        
        sum/=2;
        
        // dp[i]的含义:到目前为止i可以被组合出来
        vector<int> dp(sum+1, false);
        dp[0]=true;
        for(int& n:nums){
            // i的遍历顺序反向,确保当前元素(n)不被使用两次
            for(int i=sum;i>=n;i--)
                dp[i] = dp[i] || dp[i-n];
        }
        
        return dp[sum];
    }
};

 

posted @ 2022-04-16 21:25  Cheung-10  阅读(21)  评论(0)    收藏  举报