Leetcode 416分割等和子集

416. 分割等和子集

已知是个背包问题,由于可以等分为两部分,所以必定是个偶数。

一开始想到的是回溯法

    bool helper(vector<int>&nums, int i, int sum,int t)
    {
        if(sum==t)
            return true;
        if(i==nums.size())
            return false;
        else{
            if(sum+nums[i]<=t)
                return helper(nums,i+1,sum+nums[i],t)||helper(nums,i+1,sum,t);
            else
                return helper(nums,i+1,sum,t);
            
        }
    }
    bool canPartition(vector<int>& nums) {
        int s=0;
        for(int i:nums)
            s+=i;
        if(s&1==1)
            return false;
        int target=s/2;
        return helper(nums,0,0,target);
    }

但是这个方法存在重复计算,时间复杂度不过关,如何避免重复计算呢?每位加与不加一共有2种可能,怎么利用记忆化?必须记忆的有,和是多少;然后还需要记忆已经用了哪些数字,不能重复加自己。首先得熟悉背包问题的思想。但是背包问题是求最大值,想不到怎么转换过去。直接看答案。。。(逃
其实从递归方程应该可见端倪,递归考虑的是加和不加数字,然后递归的变量是布尔型变量,所以对于第\(i\)个数字,状态转移方程就很明显了

建立的状态是dp[i][j]i及之前的数字,能否组成j,故dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]]

    bool canPartition(vector<int>& nums) {
        int s=0;
        for(int i:nums)
            s+=i;
        if(s&1==1||nums.size()==1)
            return false;
        int target=s/2;
        vector<vector<bool>> dp(nums.size(),vector<bool>(target+1,false));
        int len=nums.size();
        dp[0][nums[0]]=true;
        for(int i=1;i<len;i++)
        {
            for(int j=0;j<=target;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];
            }
            if(dp[i][target]==true)
                return true;
        }
        return dp[len-1][target];
    }
posted @ 2019-10-09 20:21  kuadoh96  阅读(223)  评论(0编辑  收藏  举报