416. 分割等和子集 - 力扣(Leetcode)
思路:
-
使用动态规划
-
-
确定dp数组以及下标的含义
- dp[j]的定义为:容量为j的背包,所背的物品价值最大可以为dp[j]
-
确定递推公式
- 状态转移方程为
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
- 状态转移方程为
-
dp数组如何初始化
vector<int> dp(10001, 0);
-
确定遍历顺序
for(int i = 0; i < nums.size(); ++i) { for(int j = sum; j >= nums[i]; --j) { dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); } }
-
我的AC代码
// 时间复杂度O(n2),空间复杂度O(n)
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(int i = 0; i < nums.size(); ++i) {
sum += nums[i];
}
if(sum % 2 == 1) {
return false;
}
else {
sum /= 2;
}
vector<int> dp(10001, 0);
for(int i = 0; i < nums.size(); ++i) {
for(int j = sum; j >= nums[i]; --j) {
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
if(dp[sum] == sum) {
return true;
}
return false;
}
};
标准答案
// 时间复杂度O(n2),空间复杂度O(n)
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
// dp[i]中的i表示背包内总和
// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
// 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector<int> dp(10001, 0);
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
// 也可以使用库函数一步求和
// int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 == 1) return false;
int target = sum / 2;
// 开始 01背包
for(int i = 0; i < nums.size(); i++) {
for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
// 集合中的元素正好可以凑成总和target
if (dp[target] == target) return true;
return false;
}
};
Comments NOTHING