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

news2025/1/16 16:13:25

一、完全背包

题目链接/文章讲解/视频讲解:https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85.html

状态:已解决

1.问题介绍

        完全背包的模板题目:有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

        它与01背包的唯一区别在于物品的数量(01背包一种物品只有一个,而完全背包有无限个)。

2.解法

        因为完全背包只有物品数量不一样,其余都是与01背包一致的,故dp数组、递推公式都是不变的,要变的只有遍历顺序。在01背包中,我们内循环是从大到小去遍历的,母的是保证每个物品仅被添加一次。而完全背包物品是可以被添加多次的,也就是说,要从小到大去遍历。具体原因在动态规划:关于01背包问题,你该了解这些!(滚动数组)中也说过了,从小到大遍历代表第 i 个物品可以一直被添加到dp数组中。如图:

        这里还有一个问题: 为什么遍历物品在外层循环,遍历背包容量在内层循环?其实两种顺序都可以,因为dp[j] 是根据 下标j之前所对应的dp[j]计算出来的。 只要保证下标j之前的dp[j]都是经过计算的就可以了。在一维背包-CSDN博客中,我说过了,因为一维dp的写法,背包容量一定是要倒序遍历,如果遍历背包容量放在上一层,那就是先计算最后一列的数值,而我们知道某个格子的数值是依赖左上角的值的,但此时左上角(倒数第二列)还没计算,故推不出正确结果,因此只能是物品先遍历。也就是倒序遍历决定了我们只能先遍历物品。而此题并不需要倒序遍历,因此内外层哪种遍历顺序都能使得左上角和正上方的格子先被计算了。

        遍历物品在外层循环,遍历背包容量在内层循环,状态如图:

         遍历背包容量在外层循环,遍历物品在内层循环,状态如图:

3.代码实现 

#include<bits/stdc++.h>
using namespace std;
int main(void){
    int N,V;
    cin>>N>>V;
    vector<int> w(N);
    vector<int> v(N);
    for(int i=0;i<N;i++){
        cin>>w[i]>>v[i];
    }

    vector<int> dp(V+1,0);
    for(int i=0;i<N;i++){
        for(int j=w[i];j<=V;j++){
            dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
    cout<<dp[V];
}

二、518. 零钱兑换 II

题目链接/文章讲解/视频讲解:https://programmercarl.com/0518.%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2II.html

状态:已解决

1.思路 

        这道题就是典型的背包问题,且题目说了钱币的数量不限,故是背包问题中的完全背包。但此题跟纯完全背包又不一样,纯完全背包是凑成背包最大价值是多少,而本题是要求凑成总金额的物品组合个数!求的是组合数!

        我们用动规五部曲来分析此题:

(1)确定dp数组以及下标含义:

        没有多余的维度,一维数组dp就够了。dp[j]:凑成总金额为 j 的货币组合数为dp[j]。

(2)确定递推公式:

        dp[j]就是所有dp[j - coins[i]]的值相加。

        所以递推公式为:dp[j] += dp[j - coins[i]]。

(3)dp数组初始化:

        首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。如果dp[0] = 0 的话,后面所有推导出来的值都是0了。其含义是如果正好选了coins[i]后,也就是j-coins[i] == 0的情况表示这个硬币刚好能选,此时dp[0]为1表示只选coins[i]存在这样的一种选法。

(4)确定遍历顺序:

        本题中我们是外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),还是外层for遍历背包(金钱总额),内层for循环遍历物品(钱币)呢?

我在上面的完全背包理论中讲了完全背包的两个for循环的先后顺序都是可以的。但本题就不行了!

        因为纯完全背包求得装满背包的最大价值是多少,和凑成总和的元素有没有顺序没关系,即:有顺序也行,没有顺序也行!也就是说,在纯完全背包问题中,我们只关心最后的背包的总价值,不关心物品放进去的顺序

        而本题要求凑成总和的组合数,元素之间明确要求没有顺序。那么本题,两个for循环的先后顺序可就有说法了。

(1)我们先来看 外层for循环遍历物品(钱币),内层for遍历背包(金钱总额)的情况。

for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

        假设:coins[0] = 1,coins[1] = 5。那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。所以这种遍历顺序中dp[j]里计算的是组合数!

(2)外层for循环遍历背包(金钱总额),内层for遍历物品(钱币)的情况。

for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

        背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5}(j = 6, coins[i] = 1) 和 {5, 1}(j = 6, coins[i] = 5)两种情况。

此时dp[j]里算出来的就是排列数!

        排列数多于组合数,故不可以这样遍历。

       

 那为什么在纯完全背包问题中就行呢?因为对于{1,5}和{5,1},它们放进去加起来背包得到的价值都是6,故效果是相等的。

(5) 举例推导dp数组:

2.代码实现 

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);
        dp[0] = 1;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j] += dp[j-coins[i]];
            }
        }
        //for(int i=0;i<=amount;i++) cout<<dp[i]<<" ";
        return dp[amount];
    }
};

时间复杂度:O(mn)

空间复杂度:O(m) 

三、377. 组合总和 Ⅳ

题目链接/文章讲解/视频讲解:https://programmercarl.com/0377.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C%E2%85%A3.html

状态:已解决

1.思路 

        这题明面上是求组合,实际就是求排列。回溯是求排列问题的好手,但此题不需要打印集合,只需要求个数,因此还用不着回溯,动规足矣!

        这道题也是简单的完全背包问题,跟上一道题的区别无非就是一个求组合一个求排列,而我们在上道题说了组合和排列的区别实际就是遍历顺序的不同。组合的遍历顺序是先物品再容量,排序的遍历顺序是先容量再物品。因此,我们只需要在上一道题的基础上修改一下遍历顺序即可。

2.代码实现

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

时间复杂度:O(mn)   m为target的值,n为nums的长度

空间复杂度:O(m) 

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

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

相关文章

dp思维 枚举

题目链接 #include<bits/stdc.h> using namespace std; #define i64 long long const i64 mod 1e9 7; int main() {int n;cin >> n;vector<char>s(n 1);for (int i 1; i < n; i) {cin >> s[i];}//用ans记录所有满足条件的答案数量&#xff0c;c…

linux下使用qt+mpv调用GPU硬件解码

linux下GPU硬件解码接口&#xff0c;常用的有vdpau和vaapi。 mpv是基于mplayer开发的一个播放器。此外&#xff0c;mpv还提供了函数库libmpv&#xff0c;通过使用libmpv可以编写一个简单的播放器。 基于qtlibmpv的demo&#xff0c;官方例子代码如下&#xff1a;https://github.…

STM32之串口中断接收丢失数据

五六年没搞STM32了,这个项目一切都挺顺利,万万没想到被串口接收中断恶心到了。遇到的问题很奇怪 HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], LCD_UART_LEN); 这个代码中 LCD_UART_LEN=1的时候,接收过来的数据,数据包的第一个字节总是会跑到rx_buffer的末…

UE5集成gRPC

最近有项目需要在UE5里做RPC&#xff0c;对比了thrift、gRPC、rcplib等开源rpc框架&#xff0c;由于习惯使用protobuf&#xff0c;故选择了gRPC。然而&#xff0c;Google出品也是一言难尽啊&#xff0c;最起码编译太繁琐了。 本次使用的gRPC版本为1.62.1&#xff0c;UE5.2&…

面试stm32基础知识

1.ISP 第一步进入bootloader模式&#xff1a;先置BOOT0为高&#xff0c;BOOT1为低&#xff0c;再复位单片机进入bootloader模式&#xff0c;之后通过上位机下载程序&#xff1b; 第二步配置启动代码的地方&#xff1a;代码下载完毕后&#xff0c;置BOOT0为低&#xff0c;BOOT1…

【NTN 卫星通信】NTN的信关站应该建在哪些地方

1 概述 3GPP的卫星通信讨论了透传星和再生星两种方式。透传星方式&#xff0c;卫星主要是做为中继存在&#xff0c;基本上不做通信协议的处理。再生星方式&#xff0c;gNodeB的主要功能在卫星上&#xff0c;完成通信协议的主要内容。无论是透传星还是再生星&#xff0c;都需要通…

[C++]异常

基本使用 try {int a, b; std::cin >> a >> b;if(b 0) {throw std::runtime_error("unexpected zero");} else {cout << a / b << endl;}} catch(std::exception &e) { cout << "runtime_error: " << e.what() &…

战姬物语部署

一.准备环境 #关闭seliunx和防火墙 setenforce 0 systemctl stop firewalld systemctl disable firewalld #配置源&#xff0c;并安装常用工 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo curl -o /etc/yum.repos.d/epel.repo …

用队列实现栈(力扣第225题)

#include "stdio.h" #include "stdbool.h" #include "string.h" #include "stdlib.h" #include "assert.h"//初始化队列 typedef int QueueDataType;typedef struct queue {QueueDataType val;struct queue* next; }Qnode;t…

信号处理相关知识

一&#xff1a; 1.序列——三种典型序列通过matlab绘图即可 2.数字信号的自变量一定是整数&#xff0c;幅度上取值是有限的状态&#xff08;不一定是整数&#xff09;。 3.抽取和插值 4.模拟正弦信号sin(wt):w是角频率&#xff0c;单位rad/s,f是频率w/2Π。 5.假设用采样周…

盒子模型之弹性盒模型

经常适用于手机端图标布局 display: flex;让这个盒子显示成弹性盒&#xff08;很适合移动端布局&#xff09; 影响&#xff1a;1.让里面的子元素默认横向排列 2.如果子元素是行内元素&#xff0c;则直接变成块元素 3.只有一个元素&#xff0c;margin: auto;自动居中 <!DOCT…

CDPR全力开发《巫师4》,吸取《2077》教训

爆料来啦&#xff01;CDPR全力开发《巫师4》&#xff0c;吸取《2077》教训&#xff01; CD Projekt Red&#xff08;CDPR&#xff09;今天透露&#xff0c;目前全公司都在专心致志地开发《巫师4》&#xff0c;而《赛博朋克2077》的续作暂时还在早期阶段。之前参与2077的大多数…

228 基于matlab的神经网络人脸识别

基于matlab的神经网络人脸识别。 人脸识别以视网膜、 虹膜、 指纹等生物特征的识别作为生物标识符。生物特征识别不很容易伪造、 放错位置。新型脸识别使用的方法 RobustPCA 和径向基函数网络。程序已调通&#xff0c;可直接运行。 228 人脸识别 生物特征识 神经网络 - 小红书 …

12. MyBatis(二)

源码位置&#xff1a;MyBatis_demo 上篇文章我们学习了MyBatis的定义以及增删查改操作&#xff0c;并且学习了如何在xml文件中编写SQL时使用#{}的方式将参数和对象的属性映射到SQL语句中&#xff0c;上篇的内容已经足以应对大部分场景&#xff0c;本篇文章我们就要学习一下MyBa…

Jmeter 测试Dubbo接口-实例

1、Dubbo插件准备 ①把jmeter-plugins-dubbo-2.7.4.1-jar-with-dependencies.jar包放在D:\apache-jmeter-5.5\lib\ext目录 ②重新打开Jmeter客户端 在线程组-添加-取样器-dubbo simple&#xff0c;添加dubbo接口请求 2、Jmeter测试lottery接口 ①配置zookeeper参数 由于dub…

MapReduce案例-电影网站数据统计分析

本文适合大数据初学者学习MapReduce统计分析业务问题的步骤和基础的MapReduce编程方法&#xff0c;初步掌握Hadoop对计算任务的管理。 本文末尾有全部数据集和完整代码连接。 1.准备工作 安装Hadoop:Hadoop 3.3.2 离线安装-CSDN博客 按照好Hadoop之后要检查一下datanode运行情况…

Python数据容器(三)

一.tuple&#xff08;元组&#xff09; 1.元组同列表一样&#xff0c;都可以封装多个、不同类型的元素在内。 但最大的不同点在于&#xff1a;元组一旦定义完成&#xff0c;就不可修改。 2.元组定义&#xff1a;定义元组使用小括号&#xff0c;且使用逗号隔开各个数据&#…

大气的免费wordpress模板

国产的wordpress模板&#xff0c;更适合中国人使用习惯&#xff0c;更符合中国老板的审美的大气wordpress企业官网建站模板。 WordPress模板&#xff0c;也称为主题&#xff0c;是用于定义WordPress网站或博客外观和功能的预设计文件集。这些模板使用HTML、CSS和PHP代码构建&a…

ARM之栈与方法

ARM之栈与方法 计算机中的栈是一种线性表&#xff0c;它被限定只能在一端进行插入和删除操作&#xff08;先进后出&#xff09;。通常将可以插入和删除操作的一端称为栈顶&#xff0c;相对的一端为栈底。 通常栈有递增堆栈&#xff08;向高地址方向生长&#xff09;、递减堆栈…

视频监控平台的超大任务文件导入功能,如何通过日志判断导入是否成功

目录 一、概述 &#xff08;一&#xff09;编写目的 &#xff08;二&#xff09;适用情况 &#xff08;三&#xff09;导入相关参数说明 二、文件导入说明 &#xff08;一&#xff09; 日志文件路径 &#xff08;二&#xff09;不同情况下的说明和提示 1、 所有数据正确…