Leetcode 416. 分割等和子集 0,1背包问题

这题要先变换一种说法,是否可以从数组中选出一些元素来,使得这些数之和为sum/2, 显然如果sum为奇数,一定是false。

每个数有选和不选两种状态,如果直接用回溯法深搜,时间复杂度是O(2^N)

可以用动态规划来优化。

dp[i][j] 表示是否可以从前i个数字中,选出来若干个数,和为j,转移方程就是

dp[i][j] = dp[i-1][j-nums[i]] || dp[i-1][j]

注意处理边界技巧,以及剪短枝叶,如果dp[i][sum/2] = true, 那么就不用往后做了。

 

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int total = 0, n = nums.size();
        for(auto num:nums) total+=num;
        if(total%2) return false;
        int k = total/2;
        // dp[i][j] 表示前i个数是否能凑成j
        vector<vector<bool>> dp(n+1,vector<bool>(k+1));
        dp[0][0] = true;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=k;j++){
                if(j>=nums[i-1]&&dp[i-1][j-nums[i-1]]||dp[i-1][j]) dp[i][j] = true; 
            }
            // 剪枝
            if (dp[i][k]) {
                return true;
            }
        }
        return dp[n][total/2];
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值