背包

162 阅读1分钟

问题总览

类型问题完成
416. 分割等和子集
322. 零钱兑换
518. 零钱兑换 II

416. 分割等和子集

用回溯法实现了一下,发现会超时。才想到动态规划和回溯的区别。

  • 动态规划就是发现重复子问题,减少遍历的次数。
  • 回溯是真的全部遍历。
//回溯的写法
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)