zoukankan      html  css  js  c++  java
  • 套用解题模板---对背包问题的三种类分类

    对背包问题的三种类分类

    CS-Note关于动态规划的文章

    1.排列组合问题

    image-20200806101745087

    518. 零钱兑换 II (完全背包+顺序无关)

    物品的数量是无限的

    明确问题的性质

    • 背包问题
    • 组合问题(顺序是无关的)
    • 完全背包问题(可以重复选择num)

    组合问题: dp[i] += dp[i-num]

    完全背包 且 顺序无关: nums在外 target在内

    image-20200805184335769

    class Solution {
       public int change(int amount, int[] coins) {
            int n = coins.length;
            int[] dp = new int[amount + 1];
            dp[0] = 1;
    
            for (int num : coins) {
                for (int i = 1; i <= amount; i++) {
                    if (i >= num) {
                        dp[i] += dp[i - num];
                    }
                }
            }
            return dp[amount];
    
        }
    }
    

    494. 目标和(0-1背包+顺序无关)

    image-20200807223800060

    明确问题的性质

    • 背包问题
    • 组合问题(顺序没有影响)
    • 0-1背包问题(每个num只能使用一次)

    组合问题: dp[i] += dp[i-num]

    0-1背包问题,且,是组合问题(顺序不影响) nums在外, target在内,且target倒序循环

    题解

    把所有符号为正的数总和设为一个背包的容量,容量为x;

    把所有符号为负的数总和设为一个背包的容量,容量为y。

    在给定的数组中,有多少种选择方法让背包装满。
    令sum为数组的总和,则x+y = sum。
    而两个背包的差为S,则x-y=S。(S已知)

    从而求得x=(S+sum)/2。

    基于上述分析,题目转换为背包问题:给定一个数组和一个容量为x的背包,求有多少种方式让背包装满。

    class Solution {
        public int findTargetSumWays(int[] nums, int S) {
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
    
            if (S>sum || (S+sum)%2==1) return 0;
    
    
            int target = (S + sum) / 2;
            int[] dp = new int[target + 1];
            dp[0] = 1;
    
            for (int num : nums) {
                for (int i = target; i >= 0; i--) {
                    if (i >= num) {
                        dp[i] += dp[i - num];
                    }
                }
            }
    
            return dp[target];
        }
    }
    

    377. 组合总和 Ⅳ (完全背包+顺序有关)

    先给这道题定性

    • 背包问题
    • 排列问题(先后顺序是有影响的)
    • 完全背包问题(可以重复选用同一个数字)

    排列问题: dp[i] += dp[i-num]

    完全背包问题:且是 排列问题(顺序有关)

    • target在外, nums在内

      image-20200807230549786

    image-20200803132156074

    image-20200807222434147

    看懂的题解

    public int combinationSum4(int[] nums, int target) {
    
        // 状态 选择  dp  base case
        int[] dp = new int[target + 1];
        dp[0] = 1;
    
        for (int i = 1; i <= target; i++) {
            for (int num : nums) {
                if (num <= i) {
                    dp[i] = dp[i] + dp[i - num];// 上一个num选择下的组合可能数+这个num选择下可能的组合个数
                }
            }
        }
    
        return dp[target];
    
    }
    

    2.True、False问题公式

    image-20200806101907759

    416. 分割等和子集(0-1背包 + 顺序无关)

    image-20200805161053766

    可以使用动态规划解决

    也可以使用 回溯+剪枝

    明确问题性质

    • 可以是背包问题
    • 组合问题(顺序不影响)
    • 0-1背包问题(num不可重复使用)

    组合问题: dp[i] = dp[i] || dp[i-num]

    0-1背包 且 组合: nums 在外循环, target在内循环 且 逆序

    public boolean canPartition(int[] nums) {
    
            int sum = 0;
            for (int num : nums) sum += num;
    
            // sc
            if (sum % 2 != 0) return false;
            int target = sum / 2;
    
            boolean[] dp = new boolean[target + 1];
            dp[0] = true;
            
            for (int num : nums) {
                for (int i = target; i >= 0; i--) {
                    if (i >= num) {
                        dp[i] = dp[i] || dp[i - num];
                    }
                }
            }
    
            return dp[target];
    }
    

    139. 单词拆分 (完全背包+顺序有关)

    image-20200807235551161

    给问题定性

    • 是背包问题(target是字符串s, nums是字典列表)
    • true false 问题(且 顺序是有关的,同一个单词前后顺序是有影响的)
    • 完全背包(num可以重复选取)

    排列问题: dp[i] = dp[i] || dp[i-num]

    完全背包 且 顺序有关: target在外循环 nums在内循环

    public class Number39_wordBreak {
    
        // true false 问题
        // 完全背包问题
        // 顺序有关
    
        public boolean wordBreak(String s, List<String> wordDict) {
            int target = s.length();
            boolean[] dp = new boolean[target + 1];
    
            dp[0] = true;
    
            for (int i = 1; i <= target; i++) {
                for (String word : wordDict) {
                    int len = word.length();
                    if (i >= len && word.equals(s.substring(i - len, i))) {
                        dp[i] = dp[i] || dp[i - len];
                    }
                }
            }
    
            return dp[target];
        }
    }
    

    3.最大最小问题公式

    image-20200806101913679

    322. 零钱兑换 (完全背包+顺序无关)

    image-20200803220306220

    明确问题性质

    • 背包问题 (target nums)
    • 最大最小值问题
    • 完全背包问题(顺序无关)

    最大最小值问题: dp[i] = min(dp[i], dp[i-num]+1) 或者 dp[i] = max(dp[i], dp[i-num]+1)

    完全背包问题: 且 顺序无关 nums 在外循环、target在内循环

    public int coinChange(int[] coins, int amount) {
    
            int[] dp = new int[amount + 1];
            // basecase
            Arrays.fill(dp, amount + 1);
            dp[0] = 0;
    
            for (int coin : coins) {
                for (int i = 1; i <=amount ; i++) {
                    if (i >= coin) {
                        dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                    }
                }
            }
            
            return dp[amount] == (amount + 1) ? -1 : dp[amount];
    
    }
    

    474. 一和零 (01背包+顺序无关)

    image-20200815153901768

    明确问题性质

    • 最大最小值问题
    • 多维费用的 0-1 背包问题, 有两个背包, 0的数量和1的数量
    • 这里array中的字符,就相当于 nums[]中的数字
      背包容量就是拥有的数字的个数
    public int findMaxForm(String[] strs, int m, int n) {
    
            // 这是一个多为费用的0-1背包问题,
            // 有两个背包,大小是0的数量和1的数量
    
            // sc
            if (strs == null || strs.length==0) return 0;
    
            int[][] dp = new int[m + 1][n + 1];
    
            // 遍历nums
            for (String s : strs) {
                // 计算这个str有多少个0 多少个1
                int ones = 0, zeros = 0;
                for (char c : s.toCharArray()) {
                    if (c == '0') {
                        zeros++;
                    } else {
                        ones++;
                    }
                }
    //            System.out.println("ones: " + ones);
    //            System.out.println("zeros: " + zeros);
    
                // 计算不同背包容量下的可能得到的结果
                for (int i = m; i >= 0; i--) {
                    for (int j = n; j >= 0; j--) {
                        if (i >= zeros && j >= ones) {
                            dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
                        }
                    }
                }
            }
            return dp[m][n];
     }
    
  • 相关阅读:
    背包问题
    標準差、方差、正太分佈公式
    C#實現XML的增刪查改
    XML的基礎結構
    微信小程序入門學習資料鏈接
    微信小程序wxml無法實現頁面跳轉的問題
    088_jsp转成成servle所在的路径?
    075_jsp如何debug?
    028_Mybatis返回int类型为空时报错 attempted to return null from a method with a primitive return type (int)
    087_数据库类型
  • 原文地址:https://www.cnblogs.com/tangg/p/13511085.html
Copyright © 2011-2022 走看看