代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

news2025/1/11 7:47:59

代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

文章链接:完全背包理论基础 零钱兑换 II 组合总和 Ⅳ
视频链接:完全背包理论基础 零钱兑换 II 组合总和 Ⅳ

1. 完全背包理论基础

1.1 思路

在这里插入图片描述

  1. 完全背包和 01 背包的区别就是,完全背包的物品可以取无数次,也是给我们一个背包,装满之后看最大价值是多少,01 背包的物品只能取 1 次。根据背包的特性,其中的区别只体现在遍历顺序上,因此其他几步就简写了。

  2. 确定 dp 数组及其下标的含义:dp[j] 表示容量为 j 的背包,所背的物品的最大价值为 dp[j]

  3. 递推公式:dp[j]=Math.max(dp[j],dp[j-weight[i]】+value[i])

  4. dp 数组的初始化:dp[0] 表示背包容量为 0 的最大价值,就是 0 ,因为没有物品的重量为 0 可以放入背包中;非 0 下标,看递推公式 dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i])也是初始化时就应该把它初始化为非负数里的最小值即可,也就是 0

  5. 遍历顺序:因为我们之前的两层 for循环是先物品后背包,背包这一层是倒序遍历,为的就是让物品只使用一次,而我们现在可以无限使用,因此背包这一层就改为正序遍历。即 for(int i=0;i<weight.length();i++)for(int j=weight[i];j<=bagWeight;j++)bagWeight 就是背包的容量。

  6. 其实这里遍历顺序是先物品后背包,这里是可以颠倒的,可以先背包再物品。颠倒后是先确定了背包的容量再挨个放物品,从列上往下加物品,而先物品再背包则是从行上往右加物品,而我们的递推公式的第 i 个物品的 dp[j] 依赖的都是左边的 dp[j-1]dp[j-2] 这些值,而无论是从上往下还是从左往右,左边的 dp[j-1]dp[j-2] 永远有值,因此颠倒顺序是可以的。01 背包不可以是因为第 i 个物品的 dp[j] 依赖的是第 i-1 个物品的 dp 数组
    在这里插入图片描述
    在这里插入图片描述

  7. 打印 dp 数组:用于 debug

1.2 代码

//先遍历物品,再遍历背包
private static void testCompletePack(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 0; i < weight.length; i++){ // 遍历物品
        for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
        for (int j = 0; j < weight.length; j++){ // 遍历物品
            if (i - weight[j] >= 0){
                dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
            }
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

2. LeetCode 518. 零钱兑换 II

2.1 思路

  1. 本题和纯完全背包不同,纯完全背包求的是凑成背包的最大价值,本题求的是凑成总金额的组合数,注意是组合数,即 [2,1,2] 和 [2,2,1] 是一样的。这题和494. 目标和有点类似都是给一些物品和一个集合,问装满这个集合有多少种方法,和本题的区别是那题的物品只能用一次,本题的物品可以用无数次,因此那题是 01 背包,本题是完全背包
  2. dp 数组及其下标的含义:装满背包容量为 j 的背包有 dp[j] 种方法。最终求的是 dp[amount]
  3. 递推公式:就是我们放 i 个物品所对应的有多少种方法进行累加,即 dp[j]+=dp[j-coins[i]]。
  4. dp 数组的初始化:dp[0]=1,因为递推公式的通过前一个累加的,因此第一个不能为 0,为 0 就后面全是 0 了。dp[0] 的含义就是装满背包容量为 0 的方法有 1 种,空集也是一个集合。非 0 下标就是都是 0 即可,因为我们是累加的,为 0 就不会影响递推公式
  5. 遍历顺序,在上面纯完全背包上说了物品和背包的遍历顺序是可以颠倒的,但在本题就有点要求了,因为纯完全背包求的是装满后的最大价值,本题求的是装满后的最大组合数,而且还是组合,因此对遍历顺序也就有要求了。我们是先物品再背包 for(int i=0;i<coins.length;i++)for(int j=coins[i];j<=amonut;j++),dp[j]+=dp[j-coins[i]]背包这一层是正序遍历,因为物品可以使用无数次。而如果先背包后物品,就是物品在后,就会遍历多轮,从而出现重复组合的情况,如果物品在前,物品只遍历了一轮,因此本题不能颠倒顺序,求组合数就先物品再背包,求排列数就是先背包再物品。
  6. 打印 dp 数组:用于 debug

2.2 代码

class Solution {
    public int change(int amount, int[] coins) {
        //递推表达式
        int[] dp = new int[amount + 1];
        //初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
}

3. LeetCode 377. 组合总和 Ⅳ

3.1 思路

  1. 本题问用数组里的数凑成 target 有多少种方法,数组里的数可以使用无限次,因此是完全背包。本题求出的集合是讲究顺序的,也就是排列而不是组合,这题和518. 零钱兑换 II很相似,只是本题是求排列的,因此本题的动规五步曲其实就遍历顺序不一样,其他都是一样的,下面就简写了。
  2. dp 数组及其下标的含义:装满背包容量为 j 的背包有 dp[j] 种方法。最终求的是 dp[target]
  3. 递推公式:就是我们放 j 个物品所对应的有多少种方法进行累加,即 dp[i]+=dp[i-nums[j]]。
  4. dp 数组的初始化:dp[0]=1。非 0 下标就是都是 0 即可。
  5. 遍历顺序:在518. 零钱兑换 II说过了先物品再背包得到的是组合数,先背包再物品得到的是排列数,因为先背包再物品,物品在第二层循环里是每次都从开始往后取的,因此每次都能遍历多轮,因此得到的是排列数。举个例子:先物品再背后时计算 dp[4] 时只会出现 [1,3] 而不会出现 [3,1],因为是先物品再背包,3 是在 1 后面的,因此不会在 3 后面再出现 1 了。
  6. 打印 dp 数组:用于 debug
  7. 总结一下完全背包问题:对于纯完全背包,即求装满后的最大价值是什么,或者问能不能装满这个背包,那么两层 for 循环颠倒是可以的。但对完全背包问装满这个背包有多少种方法时就要区分是求的是组合数还是排列数,如果是组合数就是先物品再背包,如果是排列数就是先背包再物品

3.2 代码

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1170779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Find My手机保护壳|苹果Find My与手机保护壳结合,智能防丢,全球定位

随着科技水平的快速发展&#xff0c;科技美容这一行业做为新型产业新生而出。时尚IT品牌随着市场的多元化发展。针对手机品牌和功能的增加而呈多样化&#xff0c;将手机保护壳按质地分有PC壳&#xff0c;皮革 &#xff0c;硅胶&#xff0c;布料&#xff0c;硬塑&#xff0c;皮套…

0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发

文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 移动互联网时代的到来&#xff0c;给人们的生活带来了许多便捷和乐趣。随着用户的不断增多&#xff0c;其规模越来越大&#…

leetCode 494. 目标和 + 动态规划 + 记忆化搜索 + 递推 + 空间优化

关于本题我的往期文章&#xff1a; LeetCode 494.目标和 &#xff08;动态规划 性能优化&#xff09;二维数组 压缩成 一维数组_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://heheda.blog.csdn.net/article/details/133253822 给你一个非负整数数组 nums…

从lc114. 二叉树展开为链表到lc-LCR 155二叉搜索树转化为排序的双向链表

1 lc114. 二叉树展开为链表 1.1 描述 进阶&#xff1a;你可以使用原地算法&#xff08;O(1) 额外空间&#xff09;展开这棵树吗&#xff1f; 1.2 解法一&#xff1a; 先序遍历这棵树并且将节点加入到一个list中&#xff0c;随后按顺序将list中的每一个元素的left指针置换为…

如何保证消息只被消费一次

目录 前言 一、什么是幂等&#xff1f; 二、在生产过程中增加消息幂等性的保证 三、在消费过程中增加消息幂等性的保证 前言 消息一旦被重复消费&#xff0c;就会造成业务逻辑处理的错误。那么我们要如何避免消息的重复呢&#xff1f; 想要完全的避免消息重复的发生是很难…

全方位移动机器人 SolidWorks 转 URDF 并在 Rviz 中仿真

全方位移动机器人 SolidWorks 转 URDF 并在 Rviz 中仿真 参考 solidworks转URDF&#xff0c;并且在rviz中仿真 从solidworks导出URDF模型 Export a SolidWorks Assembly to URDF Solidworks模型导出urdf SolidWorks 模型简化 将整车除车轮部分另存为零件&#xff0c;作为一个…

MySQL连接时出现Host ‘::1‘ is not allowed to connect to this MySQL server

报错原因 之前想着要提高一下连接速度&#xff0c;所以在my.ini中加入了&#xff1a;skip-name-resolve&#xff0c;当时的数据库root账号设置的登录权限是%&#xff0c;因此没有出现连接错误&#xff0c;这次因为是新建数据库&#xff0c;root账号的登录权限默认是localhost&…

如何基于链表与数组实现栈

这里写目录标题 栈的基础知识基于数组实现栈基于链表实现栈 栈的基础知识 栈&#xff0c;又名堆栈&#xff0c;是一种受限的线性表&#xff0c;这意味着该线性表只能在一段进行插入或删除操作。具体来说&#xff0c;栈顶是允许进行插入或删除操作的一端&#xff0c;而相对的另…

零基础入门网络安全白帽黑客,挑战年薪30w!

最近好朋友老李说他想转渗透测试。 他说&#xff1a;运维这块干了3年了&#xff0c;感觉自己目前有点迷茫&#xff0c;不知该怎么去提升了。而在日常工作生活当中&#xff0c;黑客攻击可以说是很常见了&#xff0c;他感觉到网络安全越来越重要&#xff0c;对软件测试的要求也不…

大疆Livox MID-360安装ROS1/2驱动 Ubuntu20.04

文章目录 一、接线连接二、安装上位机可视化工具三、安装ROS驱动3.1 配置静态IP3.2 安装Livox SDK23.3 安装ROS驱动3.4 驱动 本文介绍如何在Ubuntu20.04中安装大疆Livox MID-360的ROS1/2驱动 一、接线连接 livox航插一分三线&#xff0c;其中航空母头连接激光雷达&#xff0c…

Spring | Sring Task (定时任务框架) 、微信小程序开发

目录&#xff1a; 一、Sring Task (定时任务框架) &#xff1a;Sring Task介绍Spring Task应用场景corn表达式corn表达式在线生成器SpringTask入门案例&#xff1a;导入maven依赖启动类上添加 EnableScheduling 注解定时方法上添加 Scheduled( cron “xxxxx” ) 注解自定义“定…

ZKP Introduction of Nova (Yu Guo) 手写笔记

ZKP学习笔记 郭宇老师Nova课程手写笔记

创建asp.net api和docker-compose项目

vs2022创建asp.net core web api项目 创建完成 添加docker-compose支持 添加成功 docker配置 docker-compose配置

腾讯云3年轻量2核2G4M服务器从366.6元三年涨价了?

2023腾讯云双11优惠活动3年轻量应用服务器涨价了&#xff1f;确实是涨价了&#xff0c;仅限于三年时长轻量应用服务器&#xff0c;一年时长并没有涨价&#xff0c;相比隔壁阿里云&#xff0c;腾讯云依旧在提供三年轻量应用服务器和5年时长云服务器CVM已经很难得了&#xff0c;想…

集线器、交换机、网桥、路由器、网关

目录 集线器(HUB)交换机(SWITCH)网桥(BRIDGE)路由器(ROUTER)网关(GATEWAY)交换机和路由器的区别参考 集线器(HUB) 功能 集线器对数据的传输起到同步、放大和整形的作用 属于物理层设备 工作机制 使用集线器互连而成的以太网被称为共享式以太网。当某个主机要给另一个主机发送单…

如何使用 SwiftUI 中新地图框架 MapKit

文章目录 前言MapKit 弃用项MapContentBuilder&#xff08;iOS 17&#xff09;地图交互地图样式地图控件地图相机位置总结 前言 了解 iOS 17 中的 MapKit 后&#xff0c;我们会发现 Apple 引入了更适合 SwiftUI 的 API。 MapKit 弃用项 一旦将你的 App 目标更新到 iOS 17&am…

OpenGL ES入门教程(二)之绘制一个平面桌子

OpenGL ES入门教程&#xff08;二&#xff09;之绘制一个平面桌子 前言0. OpenGL绘制图形的整体框架概述1. 定义顶点2. 定义着色器3. 加载着色器4. 编译着色器5. 将着色器链接为OpenGL程序对象6. 将着色器需要的数据与拷贝到本地的数组相关联7. 在屏幕上绘制图形8. 让桌子有边框…

立创eda 焊接辅助工具使用

立创EDA为板级EDA设计软件。EDA指的是通过计算机的辅助完成电路原理图、印刷电路板文件等的绘制、制作、仿真设计。 立创EDA是一款基于浏览器的&#xff0c;专为中国人设计的&#xff0c;友好易用的EDA设计工具。起于2010年&#xff0c;完全由中国人独立开发&#xff0c;拥有独…

Ubuntu下安装vscode,并解决终端打不开vscode的问题

Visual Studio Code安装 1&#xff0c;使用 apt 安装 Visual Studio Code 在官方的微软 Apt 源仓库中可用。按照下面的步骤进行即可&#xff1a; 以 sudo 用户身份运行下面的命令&#xff0c;更新软件包索引&#xff0c;并且安装依赖软件&#xff1a; sudo apt update sud…

女孩子穿这种粉粉嫩嫩~的卫衣也太好看了吧

果然女孩子穿这种粉粉嫩嫩的衣服 真的超级有甜美可爱氛围哎 软糯亲肤的面料&#xff0c;上身很舒服哦 时尚polo领加上半拉链设计 既实用又美观&#xff0c;穿脱很方便