代码随想录二刷第十四天 | 完全背包专题

54 阅读4分钟

52. 携带研究材料

题目:52. 携带研究材料(第七期模拟笔试)

题解:代码随想录

状态:需要多复习

思路

  • dp数组含义:dp[i][j]表示从下标为[0-i]的物品,各物品可以取无限次,放进容量为j的背包,价值总和最大值
  • 递推公式:不放物品i对比放物品i,dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
  • 初始化:由递推公式知,需初始化dp[i][0]=0,以及dp[0][j]

代码

时间复杂度:O(N*V) 空间复杂度:O(N*V)

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int v = in.nextInt();
        int[] weight = new int[n];
        int[] value = new int[n];
        for(int i = 0; i < n; i++){
            weight[i] = in.nextInt();
            value[i] = in.nextInt();
        }

        int[][] dp = new int[n][v + 1];
        for(int i = weight[0]; i <= v; i++){
            dp[0][i] = dp[0][i - weight[0]] + value[0];
        }

        for(int i = 1; i < n; i++){
            for(int j = 1; j <= v; j++){
                if(j < weight[i]) dp[i][j] = dp[i - 1][j];
                else dp[i][j] = Math.max(
                    dp[i][j - weight[i]] + value[i],
                    dp[i - 1][j]
                );
            }
        }
        System.out.println(dp[n - 1][v]);
    }
}

518.零钱兑换II

题目:518. 零钱兑换 II - 力扣(LeetCode)

题解:代码随想录

状态:AC,一维数组的情况需要复习

思路

二维

  • 递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
  • dp数组初始化:第一列初始化为1和第一行初始化
  • 遍历顺序:二维的遍历顺序随便

一维

  • 递推公式:dp[j] += dp[j - coins[i]]
  • dp数组初始化:dp[0] = 1,表示amount为0时有一种找零方式,即不找零
  • 遍历顺序:先种类再容量为组合数,否则为排列数

代码(二维dp数组)

时间复杂度:O(N*Amount) 空间复杂度:O(N*Amount)

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

代码(一维dp数组)

时间复杂度:O(N*Amount) 空间复杂度:O(Amount)

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

377. 组合总和 Ⅳ

题目:377. 组合总和 Ⅳ - 力扣(LeetCode)

题解:代码随想录

状态:需要多复习

思路

与组合数的区别在于遍历顺序,此处是先背包容量,再硬币种类

代码

时间复杂度:O(N^2) 空间复杂度:O(N)

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for(int i = 1; i <= target; i++){
            for(int j = 0; j < nums.length; j++){
                if(i >= nums[j]){
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
}

57. 爬楼梯

题目:57. 爬楼梯(第八期模拟笔试)

题解:代码随想录

状态:AC

思路

同上题

代码

时间复杂度:O(N^2) 空间复杂度:O(N)

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();

        int[] dp = new int[n + 1];
        dp[0] = 1;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j ++){
                if(j <= i) dp[i] += dp[i - j];
            }
        }
        System.out.println(dp[n]);
    }
}

322. 零钱兑换

题目:322. 零钱兑换 - 力扣(LeetCode)

题解:代码随想录

状态:半AC

思路

需要注意将无法凑出的数替换成amount+1

代码

时间复杂度:O(N^2) 空间复杂度:O(N)

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

279. 完全平方数

题目:279. 完全平方数 - 力扣(LeetCode)

题解:代码随想录

状态:需要多复习

思路

注意初始化和遍历条件

代码

时间复杂度:O(N^2) 空间复杂度:O(N)

class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n + 1];
        Arrays.fill(dp, n + 1);
        dp[0] = 0;
        for(int i = 1; i * i <= n; i++){
            for(int j = i * i; j <= n; j++){
                dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
            }
        }
        return dp[n];
    }
}

139.单词拆分

题目:139. 单词拆分 - 力扣(LeetCode)

题解:代码随想录

状态:需要多复习

思路

  • dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词
  • if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true
  • dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false
  • 相当于求排列数的变体,所以要先遍历背包再遍历物品

代码

时间复杂度:O(N^2) 空间复杂度:O(N)

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        HashSet<String> set = new HashSet<>(wordDict);
        boolean[] valid = new boolean[s.length() + 1];
        valid[0] = true;

        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i && !valid[i]; j++) {
                if (set.contains(s.substring(j, i)) && valid[j]) {
                    valid[i] = true;
                }
            }
        }

        return valid[s.length()];
    }
}