问题总览
| 类型 | 问题 | 完成 |
|---|---|---|
| 416. 分割等和子集 | ||
| 322. 零钱兑换 | ||
| 518. 零钱兑换 II |
用回溯法实现了一下,发现会超时。才想到动态规划和回溯的区别。
- 动态规划就是发现重复子问题,减少遍历的次数。
- 回溯是真的全部遍历。
//回溯的写法
class Solution {
boolean ans = false;
public boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum % 2 != 0) {
return false;
}
// 转化为是否存在总和为k的子集,有重复元素,不可重复取
Arrays.sort(nums);
backtrace(nums, sum / 2, 0, 0);
return ans;
}
public void backtrace(int[] nums, int k, int sum, int idx) {
if (sum == k) {
ans = true;
return;
}
if (sum > k) {
return;
}
for (int i = idx; i < nums.length; i++) {
if (i > idx && nums[i] == nums[i - 1]) {
continue;
}
sum += nums[i];
backtrace(nums, k, sum, i + 1);
sum -= nums[i];
}
}
}
//leetcode submit region end(Prohibit modification and deletion)
//动态规划
class Solution {
boolean ans = false;
public boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum % 2 != 0) {
return false;
}
// 当背包容量为j时,前i个物品能否将背包装满
// dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]]
int n = nums.length;
sum = sum / 2;
boolean[][] dp = new boolean[n + 1][sum + 1];
for (int i = 0; i <= n; i++) {
dp[i][0] = true;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
if (j < nums[i - 1]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[n][sum];
}
}
//leetcode submit region end(Prohibit modification and deletion)