代码随想录算法训练营第三十七天 | 377. 组合总和 Ⅳ | 377. 组合总和 Ⅳ | 322. 零钱兑换 | 279.完全平方数 | 138.单词拆分

Day 37 总结

  • 自己实现中遇到哪些困难
    • 如果求组合数就是外层for循环遍历先物品,内层for遍历背包。
    • 如果求排列数就是外层for遍历先背包,内层for循环遍历物品。
  • 今日收获,记录一下自己的学习时间
    • 20:15 - 21:00

小总结

  • 组合总和 Ⅳ: 求组合先物品, 求排列先背包
  • 爬楼梯 (进阶): 确定物品是什么, 物品重量是什么. 物品价值是什么, 背包容量是什么
  • 求方法数量: dp[i] += dp[i - nums[j]];
  • 零钱兑换: 求组合的最小长度, dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
  • 完全平方数: 求组合最小长度,确定物品,确定背包

多重背包

给一些物品,但是物品的数量有限.不能无限的去取了.

可以再做完全背包的时候,去计数,

        先物品,再容量,再次数(j - k * weight[i])

for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++)

然后也能摊开拆成01背包.

背包问题总结

  • 背包递推公式

    • 能否装满背包: dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])

    • 装满背包有几种方法:dp[j] += dp[j - nums[i]]

    • 背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    • 装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

  • 遍历顺序

    • 01背包: 二维数组,从小到大,不在意顺序; 一维数组,先物品,再背包(从大到小)

    • 完全背包: 求组合,先物品再背包,求排列,先背包再物品

377. 组合总和 Ⅳ

视频讲解:动态规划之完全背包,装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV_哔哩哔哩_bilibili

代码随想录

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

题目描述:

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

请注意,顺序不同的序列被视作不同的组合。

顺序对这个题目来说很重要, 如果先遍历数字, 再遍历容量, 不会发现 112 和 211 的区别.

实现思路:

物品 = 数字,物品重量 = 数字大小, 背包容量 = 目标整数。数字无限取 = 完全背包。

dp数组:dp[i][j] 数字 i,填满容量为 j 的背包,有多少种方式。

推导公式: dp[j] = dp[j] + dp[j - num]

初始化:dp[0] = 1. 填满背包0的方式为什么数字都不选.

遍历顺序:依次遍历数字, 容量从小到达遍历背包

代码实现:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];

        dp[0] = 1;

        for (int j = 0; j<target+1; j++) {
            for (int i=0; i<nums.length; i++) {
                if (j - nums[i] < 0) {continue;}
                dp[j] += dp[j - nums[i]];
                // System.out.println(Arrays.toString(dp));
            }
            // System.out.println();
        }

        return dp[target];
    }
}

70. 爬楼梯 (进阶)

代码随想录

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

题目描述:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 

每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢? 

注意:给定 n 是一个正整数。

实现思路:

物品 = 楼梯, 物品重量 = 楼梯层数, 背包容量 = 爬到第几层. 楼梯层数任选, 完全背包.

dp数组:dp[j] 代表爬第 n 层楼梯有的方法数量

推导公式: dp[j] = dp[j] + dp[j - 本次爬的层数]

初始化:dp[0] = 1. 爬0层只有一种方法,就是不爬.

遍历顺序:先爬1层再爬3层, 和先爬3层再爬1层 是不一样的. 本题求排列,在意顺序. 先背包后物品.

代码实现:

import java.util.Scanner;
import java.util.Arrays;

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 j=1; j<=n; j++)        
            for (int i=1; i<=m; i++) {
                if (j < i) continue;
                dp[j] += dp[j - i];
        }
        System.out.println(dp[n]);
    }
}

 322. 零钱兑换

代码随想录

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

题目描述:

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

实现思路:

物品 = 硬币, 物品重量 = 硬币价值, 背包容量 = amount总金额. 硬币数量不限,完全背包. 

求最少硬币数量,{2,1}和{1,2}相同,不在意顺序,求组合的最小长度, 先物品再背包.

dp数组:dp[j] 使用当前的硬币装满容量j需要的最少数量.

推导公式: dp[j] = Math,min(dp[j], dp[j - 硬币价值]+1)

初始化:求最小,所以先把每个格子初始化为最大整数值.

遍历顺序:不在意顺序,先物品,再背包.

代码实现:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        for (int i=1; i<amount+1; i++) dp[i] = Integer.MAX_VALUE;

        for (int i=0; i<coins.length;i ++) {
            for (int j=coins[i]; j<amount+1; j++) {
                if (dp[j - coins[i]] == Integer.MAX_VALUE) {continue;} // 防止溢出
                dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
            }
        }
        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount]; // 检查都没有解
    }
}

 279.完全平方数

代码随想录

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

题目描述:

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,149 和 16 都是完全平方数,而 3 和 11 不是。

实现思路:

物品 = 完全平方数, 物品重量 = 完全平方数的值, 背包容量 = n.

求填满背包的最少元素数量, 求组合长度, 先物品再背包.

dp数组:dp[j] 当前元素填满背包需要的最少数量

推导公式: dp[j] = Math.min(dp[j], dp[j - 完全平方数值] + 1)

初始化:求最少数量, 先初始化为最大值

遍历顺序:先物品,再背包. 物品为 1^2, 2^2, 3^2...., 背包容量 1 .... n

代码实现:

class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n+1];
        for (int i=1; i<n+1; i++) {dp[i] = Integer.MAX_VALUE;}

        for (int i=1; i*i <= n; i++) {
            for (int j=1; j<= n; j++) {
                if (j - i*i < 0) {continue;}
                dp[j] = Math.min(dp[j], dp[j - i*i] + 1);
            }
        }
        return dp[n];
    }
}

139.单词拆分

代码随想录

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

题目描述:

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

实现思路:

如果字符串可以由字典里的单词组成,那么必然会先遇到一个符合条件的字符串, 然后再遇到一个符合条件的字符串,然后再遇到一个符合条件的字符串.

使用多个物品填满一个东西, 背包问题, 物品随便用, 完全背包.

物品 = 字典里的单词, 物品重量 = 单词长度且匹配的上, 背包容量 = 字符长度

dp数组:dp[j] , 字符串 0 ~ j 能否匹配的上字典里的一个词. (左开右闭)

推导公式:

        如果 dp[i] 为 true, 前置字符串能够匹配的上

        查看字典里的哪些词可以接上, 将匹配到的长度设置为 true

初始化:dp[0] = true, 空串什么单词都不放也能匹配成功.

遍历顺序:求排列, apple + pen + apple != apple + apple + pen, 先背包,再物品

        肯定不能先物品,因为物品要用在不同的位置,先再每一个位置尝试匹配字典里的所有词.

代码实现:

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

        for (int i=0; i<=s.length(); i++) {
            for (String word : wordDict) {
                if (dp[i] // 能够匹配到前置字符串
                    && i+word.length() <= s.length()  // 待匹配字符串
                    && s.substring(i, i+word.length()).equals(word)) // 匹配成功
                    dp[i+word.length()] = true;
            }
        }

        return dp[s.length()];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值