这题要先变换一种说法,是否可以从数组中选出一些元素来,使得这些数之和为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];
}
};