力扣动态规划专题(三)完全背包 518.零钱兑换II 377. 组合总和 Ⅳ 70. 爬楼梯 322. 零钱兑换 279.完全平方数 步骤及C++实现

news2024/11/16 9:26:57

文章目录

  • 完全背包
    • 一维dp数组 滚动数组
  • 518.零钱兑换II
  • 377. 组合总和 Ⅳ
  • 70. 爬楼梯
  • 322. 零钱兑换
  • 279.完全平方数
  • 139.单词拆分

完全背包

在这里插入图片描述
完全背包的物品数量是无限的,01背包的物品数量只有一个
完全背包和01背包分许步骤一样,唯一不同就是体现在遍历顺序

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个,也就是一个物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。

背包最大重量为4,问物品有无限个,那么背包能背的物品最大价值是多少?物品为:

重量价值
物品0115
物品1320
物品2430

一维dp数组 滚动数组

  1. 确定dp数组以及下标的含义:一维数组dp[j],容量为j的背包,所背的物品价值可以最大为dp[j]
背包重量j01234
物品0
物品1
物品2
  1. 确定递推公式,有两个方向推出来dp[j]:
  • 不放物品i:由dp[j]本身推出,物品i的重量 > 背包j的重量,物品i无法放进背包中,背包内的价值不变。
  • 放物品i:由dp[j - weight[i]] + value[i]推出,物品i的重量 < 背包j的重量,物品i可以放进背包中,背包内的价值为dp[j - weight[i]] + value[i]
  • 递归公式: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
  1. dp数组如何初始化
    情况1:j=0时,dp[0]=0,此时背包容量j为0,无论选取什么物品,背包价值总和为0
    情况2:j≠0时,dp[j]会被覆盖更新。
背包重量j01234
物品0015151515
物品10
物品20
  1. 确定遍历顺序
    和01背包最大区别在于遍历顺序
  1. 遍历顺序
  • 01背包的二维dp数组的内外for循环遍历顺序可以互换,先遍历物品或者先遍历背包
  • 01背包的一维dp数组的外层for循环只能先遍历物品,内循环遍历背包。且内循环从大到小遍历,为了保证每个物品仅被添加一次
  • 纯完全背包的一维dp数组内外for循环遍历顺序也可以互换,并且完全背包的物品是可以添加多次的,所以要从小到大去遍历
  • 不是纯完全背包,内外for循环不可以互换,如题518
//01背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}

//完全背包 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}

//完全背包 先遍历背包,再遍历物品
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
    cout << endl;
}
  1. C++实现
void test_CompletePack() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;
    vector<int> dp(bagWeight + 1, 0);
    // 先遍历物品,在遍历背包
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    /*
    // 先遍历背包,再遍历物品
    for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
        for(int i = 0; i < weight.size(); i++) { // 遍历物品
            if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }*/
    cout << dp[bagWeight] << endl;
}
int main() {
    test_CompletePack();
}

518.零钱兑换II

在这里插入图片描述

注意题目说的是组合数,5 = 2 + 2 + 1与5 = 2 + 1 + 2是同一种组合,但是两种排列。

如果求组合数就是外层for循环遍历物品,内层for循环遍历背包。

如果求排列数就是外层for循环遍历背包,内层for循环遍历物品。

步骤

  1. 确定dp数组以及下标的含义
    dp[j]:凑成总金额j的货币组合数为dp[j]

  2. 确定递推公式
    dp[j] 就是所有的 dp[j - coins[i]](考虑coins[i])的情况相加,递推公式:dp[j] += dp[j - coins[i]];
    组合问题推导公式都类似dp[j] += dp[j - nums[i]];

  3. dp数组如何初始化
    j=0时,dp[0]=1,表示只能选coins[i]硬币,且dp[0]=1是递归公式的基础,否则后面推导的值都为0
    j≠0时,dp[j]=0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j]

  4. 确定遍历顺序

  • 外for循环遍历物品(钱币),内层for遍历背包(金钱总额),计算的是组合数,只有{1, 3},不会出现{3, 1}
  • 外for遍历背包(金钱总额),内层for循环遍历物品(钱币),计算的是排列数,会出现{1, 3} 和 {3, 1}两种情况
  • 如果外循环遍历物品coins,内循环遍历背包amount,计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为conis遍历放在外层,3只能出现在1后面
  1. 举例推导dp数组
    输入: amount = 5, coins = [1, 2, 5]

  2. C++实现

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1, 0);
        dp[0] = 1;//初始化
        //组合数 先物品coins 后背包amount
        for(int i=0; i<coins.size(); i++)
        {
            for(int j=coins[i]; j<=amount; j++)
            {
                dp[j] += dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ

在这里插入图片描述

注意题目说顺序不同的序列被视作不同的组合,求的是排列数,那么外层for遍历背包,内层for循环遍历物品。

如果要把所有排列都列出来,只能使用回溯算法

步骤

  1. 确定dp数组以及下标的含义
    dp[i]:凑成目标正整数为i的排列个数为dp[i]

  2. 确定递推公式
    dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来,递推公式:dp[j] += dp[j - nums[i]];
    组合问题推导公式都类似dp[j] += dp[j - nums[i]];

  3. dp数组如何初始化
    i=0时,dp[0]=1,没有意义,仅为了避免后面推导的值都为0
    i≠0时,dp[i]=0,这样累计加 dp[i - nums[j]] 的时候才不会影响真正的 dp[i]

  4. 确定遍历顺序

  • 外for循环遍历物品,内层for循环遍历背包,计算的是组合数——518题、494题
  • 外for循环遍历背包,内层for循环遍历物品,计算的是排列数——本题
  1. 举例推导dp数组
    输入: target=4, nums = [1, 2, 3]

  2. C++实现

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1, 0);
        dp[0] = 1;
        //排列数 先背包target 后物品nums
        for(int i=0; i<=target; i++)
        {
            for(int j=0; j<nums.size(); j++)
            {
                if(i-nums[j] >= 0 && dp[i] < INT_MAX - dp[i-nums[j]]) dp[i] += dp[i-nums[j]];
            }
        }
        return dp[target];
    }
};

70. 爬楼梯

在这里插入图片描述
注意题目给的示例2中,1阶+2阶 和 2阶+1阶 是不同的组合,求的是排列数,外层for遍历背包,内层for循环遍历物品。

步骤

  1. 确定dp数组以及下标的含义
    dp[i]:爬到有i个台阶的楼顶,有dp[i]种方法

  2. 确定递推公式
    dp[i] 由 dp[i - j] 推导出来,递推公式:dp[i] += dp[i - j];
    组合问题推导公式都类似dp[j] += dp[j - nums[i]];

  3. dp数组如何初始化
    i=0时,dp[0]=1,避免后面推导的值都为0
    i≠0时,dp[i]=0,这样累计加 dp[i - j] 的时候才不会影响真正的 dp[i]

  4. 确定遍历顺序

  • 外for循环遍历物品,内层for循环遍历背包,计算的是组合数——518题、494题
  • 外for循环遍历背包,内层for循环遍历物品,计算的是排列数——本题、377题
  1. 举例推导dp数组
    输入: n=4

  2. C++实现

class Solution {
public:
    int climbStairs(int n) {
        /*
        //01背包 1.只维护两个数值
        if(n <= 1) return n;
        int dp[3];
        dp[1] = 1;
        dp[2] = 2;
        int sum = 0;
        for(int i=3; i<=n; i++)
        {
            sum = dp[1] + dp[2];
            dp[1] = dp[2];
            dp[2] = sum;
        }
        return dp[2];

        //01背包 2.维护整个数组
        if(n <= 1) return n;
        vector<int> dp(n+1);
        dp[1] = 1;
        dp[2] = 2;
        for(int i=3; i<=n; i++)
        {
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];*/

        //完全背包
        vector<int> dp(n+1, 0);
        dp[0] = 1;
        //排列数 先背包 后物品 
        //内层for循环的2表示一次最多可以爬2层台阶
        for (int i = 1; i <= n; i++) { // 遍历背包
            for (int j = 1; j <= 2; j++) { // 遍历物品
                if (i - j >= 0) dp[i] += dp[i - j];
            }
        }
        return dp[n];
    }
};

如果题目改为:一步一个台阶,两个台阶,三个台阶,…,直到 m个台阶。问有多少种不同的方法可以爬到楼顶?

  • 1阶,2阶,… m阶就是物品,楼顶就是背包。每一阶可以重复使用,跳了1阶,还可以继续跳1阶
  • 问跳到楼顶有几种方法其实就是问装满背包有几种方法——完全背包
  • C++实现时,可以把完全背包方法中的 内层for循环中的2改成对应的m

322. 零钱兑换

在这里插入图片描述
注意题目说每种硬币的数量是无限的,完全背包

步骤

  1. 确定dp数组以及下标的含义
    dp[j]:凑足总额为j所需钱币的最少个数为dp[j]

  2. 确定递推公式
    dp[j](考虑coins[i]),由dp[j - coins[i]]推导而来,再加上一个钱币coins[i],就是dp[j]
    同时,dp[j] 取所有 dp[j - coins[i]] + 1 中最小的,递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

  3. dp数组如何初始化
    j=0时,dp[0]=0,凑足总金额为0所需钱币的个数一定是0
    j≠0时,dp[j]=INT_MAX,dp[j]必须初始化为一个最大的数,否则在 min(dp[j - coins[i]] + 1, dp[j])比较中会被初始值覆盖

  4. 确定遍历顺序

  • 外for循环遍历物品,内层for循环遍历背包,计算的是组合数——518题、494题
  • 外for循环遍历背包,内层for循环遍历物品,计算的是排列数——377题、70题
  • 本题并不强调是组合数还是排列数,只需要钱币个数最小,是纯完全背包问题。因此先遍历物品或者先遍历背包,并且内循环正序遍历
  1. 举例推导dp数组
    输入: coins = [1, 2, 5], amount = 5

  2. C++实现

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, INT_MAX);
        dp[0] = 0;
        //先物品 后背包
        for(int i=0; i<coins.size(); i++)
        {
            for(int j=coins[i]; j<=amount; j++)
            {
                if(dp[j - coins[i]] != INT_MAX) dp[j] = min(dp[j], dp[j-coins[i]]+1);
            }
        }
        //先背包 后物品
        /*for(int i=0; i<=amount;i++)
        {
            for(int j=0; j<coins.size(); j++)
            {
                if(i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX) dp[i] = min(dp[i], dp[i-coins[j]]+1);
            }
        }*/
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

279.完全平方数

在这里插入图片描述
完全平方数就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品,完全背包问题

步骤

  1. 确定dp数组以及下标的含义
    dp[j]:和为j的完全平方数的最少数量为dp[j]

  2. 确定递推公式
    dp[j],由 dp[j - i * i] 推导而来,再加上1就是dp[j]

同时,dp[j] 取所有 dp[j - i * i] + 1 中最小的,递推公式:dp[j] = min(dp[j - i * i] + 1, dp[j]);

  1. dp数组如何初始化
    j=0时,dp[0]=0
    j≠0时,dp[j]=INT_MAX,dp[j]必须初始化为一个最大的数,否则在 min(dp[j - coins[i]] + 1, dp[j])比较中会被初始值覆盖

  2. 确定遍历顺序

  • 外for循环遍历物品,内层for循环遍历背包,计算的是组合数——518题、494题
  • 外for循环遍历背包,内层for循环遍历物品,计算的是排列数——377题、70题
  • 题目并不强调是组合数还是排列数,纯完全背包问题。因此先遍历物品或者先遍历背包,并且内循环正序遍历——本题、322题
  1. 举例推导dp数组
    输入: n = 5

  2. C++实现

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 0;
        //先背包 后物品
        for(int i=0; i<=n; i++)
        {
            for(int j=1; j*j<=i; j++)
            {
                dp[i] = min(dp[i], dp[i-j*j]+1);
            }
        }
        
        //先物品 后背包
        /*for(int i=1; i*i<=n; i++)
        {
            for(int j=i*i; j<=n ;j++)
            {
                dp[j] = min(dp[j], dp[j-i*i]+1);
            }
        }*/
        return dp[n];
    }
};

139.单词拆分

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

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

相关文章

deque的介绍

前言 为什么会存在deque呢&#xff1f;在c标准库中deque是作为 stack和queue的底层容器就是deque&#xff0c;我们要是了解过list和vector就会知道这两种容器各有优劣&#xff0c;vector的优点是支持随机访问&#xff0c;进而可以支持排序和二分查找等算法&#xff0c;它的缺点…

鼠标事件 获取鼠标坐标及点击事件

运行效果&#xff1a; 头文件 #ifndef MOUSEEVENT_H #define MOUSEEVENT_H #include #include #include #include class MouseEvent : public QMainWindow { Q_OBJECT public: MouseEvent(QWidget *parent 0); ~MouseEvent(); protected: void mousePressEvent(QMouse…

Linux:一键搭建ftp服务(vsftpd)

《TRO-23614-VSFTPD》 如果csdn收费 可以和我si liao 我会免费发给你 我发的这个是一个tar归档包&#xff0c;脚本就在其中 Linux&#xff1a;《tar》归档命令_鲍海超-GNUBHCkalitarro的博客-CSDN博客 tar xfz tarro_vsftpd.tar.gz -C /root/ # tar xfz tar包路径 -…

SpringBoot第27讲:SpringBoot集成MySQL - MyBatis 多个数据源

SpringBoot第27讲&#xff1a;SpringBoot集成MySQL - MyBatis 多个数据源 本文是SpringBoot第27讲&#xff0c;在某些场景下&#xff0c;Springboot需要使用多个数据源&#xff0c;以及某些场景会需要多个数据源的动态切换。本文主要介绍上述场景及 SpringBootMyBatis实现多个数…

【期末满分作业】C语言程序设计 实训1——奖学金评定系统的设计与实现(附带实验报告、源码以及解释)

大家好&#xff0c;各位努力奋斗的大学生小伙伴们&#xff01;今天&#xff0c;我将带你们领略一项令人惊叹的程序设计奇迹——《奖学金评定系统》&#xff01;是不是感到激动呢&#xff1f;别急&#xff0c;让我为你们揭开这个能让你在C语言程序设计中拿满分的秘密武器&#x…

ASP.NET Core MVC 从入门到精通之Identity入门

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

pikache靶场通关——XSS漏洞

文章目录 前言环境第一关、反射型xss(get)Step.1、输入特殊字符测试Step.2、输入js语句Step.3、在URL中输入js语句 第二关、反射性xss(post)Step.1、输入获取cookie的js语句 第三关、存储型xssStep.1、输入获取cookie的js语句Step.2、查看页面源码Step.3、感受危害性 第四关、D…

1 君正IPC芯片方案介绍

专栏特色 1、所有源码严格遵守统一的编码规范。 2、手把手教学&#xff0c;让你从零开始&#xff0c;深入了解君正方案IPC库的方方面面。 3、纯C接口&#xff0c;接口封装严谨&#xff0c;接口功能丰富&#xff0c;应用层调用简单便捷。 4、近二十年行业经验和技术积累打造的高…

风电光伏iEEE33节点蒙特卡洛概率潮流计算

基于蒙特卡洛法的概率潮流 以IEEE33节点的电网为研究对象 建立了光伏和风电的概率出力模型 采用蒙特卡洛法进行随机抽样 之后基于抽样序列进行概率潮流计算 最后得到电网的电压概率出力曲线

使用数字钥匙技术的车辆有多安全?

首发微信公众号网络研究院&#xff0c;关注获取更多。 虽然有几种不同的方法来实现汽车使用的数字钥匙&#xff0c;但安全的数字钥匙标准应该利用近场通信 (NFC) 和超宽带 (UWB) 结合蓝牙低功耗 (BLE) 来访问车辆&#xff0c;开始引擎&#xff0c;固定车辆&#xff0c;或授权各…

云原生之使用Docker部署Dashy个人导航页

云原生之使用Docker部署Dashy个人导航页 一、Dashy介绍1.1 Dashy简介1.2 Dashy特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、部署前准备工作4.1下载Dashy源码包4.2 查看D…

vue基础-ref (!)

&#xff01;1、ref 引用 在父组件中直接去调 子组件中的方法&#xff0c;使用ref 很简单&#xff0c;比父子传值 简单很多 1、使用ref引用DOM元素 第一步&#xff1a;给标签里 写 ref“xxx” 第二步&#xff1a;就可以用this.$refs.xxx 来拿到这个元素 使用 ! 2、使用ref引…

Spring - Bean的6种作用域

1、Bean作用域问题2、作用域定义2.1、Bean的6种作用域singleton 单例模式prototype 原型作用域request 请求作用域session 会话作用域application 全局作用域&#xff08;了解&#xff09;websocket 单例作用域 vs 全局作用域 2.设置作用域 1、Bean作用域问题 通过一个案例来看…

[工业互联-10]:PLC入门简介

目录 前言 PLC的用途 PLC的特点 PLC的分类 1、按PLC的控制规模分类 2、按PLC的控制性能分类 3、按PLC的结构分类 PLC的技术指标 1、硬件指标 2、软件指标 3、主要性能指标介绍 1) 存储容量 2) 输入/输出&#xff08;I/O&#xff09;点数 3) 扫描速度 4) 指令的功…

【Redis】五种数据结构

在内存种种存储形式如下&#xff1a;

练习2:逻辑回归

练习2&#xff1a;逻辑回归 介绍 在本练习中&#xff0c;您将实现逻辑回归并将其应用于两个不同的数据集。还将通过将正则化加入训练算法&#xff0c;来提高算法的鲁棒性&#xff0c;并用更复杂的情形来测试模型算法。 在开始练习前&#xff0c;需要下载如下的文件进行数据上…

90后程序员回家卖羊粪,月销售额120万!

不得不说&#xff0c;程序员是一个勤奋而又善于思考的群体。他们不只是代码写得好&#xff0c;善于逻辑思维&#xff0c;即使有一天不做程序员&#xff0c;转行其他岗位了&#xff0c;也能在新的岗位上面玩出花样。 早在2013年的时候&#xff0c;就有一位新浪的PHP程序员转行卖…

单点登录:CAS使用springboot main方法启动cas-server

1.下载demo git clone https://gitee.com/pelin0963/cas-server.git2.使用eclipse导入maven项目。此次我是用的spring tool suite 4导入的。 导入时会用较长时间&#xff0c;10分钟吧。需要下载很多资源。 3.报错&#xff0c;提示缺少jar包 Missing artifact net.shibbolet…

【java】使用 BeanUtils.copyProperties 11个坑(注意事项)

文章目录 背景第1个坑&#xff1a; 类型不匹配第2个坑: BeanUtils.copyProperties是浅拷贝第3个坑&#xff1a;属性名称不一致第4个坑&#xff1a;Null 值覆盖第5个坑&#xff1a;注意引入的包第6个坑&#xff1a;Boolean类型数据is属性开头的坑第7个坑&#xff1a;查找不到字段…

C语言strlen函数的缺陷与实现,strcpy函数的缺陷与实现,strcat函数的缺陷与实现,strcmp的实现。

1.strlen 函数原型&#xff1a; size_t strlen( const char *string );size_t 是无符号整型&#xff0c;相当于unsigned intconst char *string 是目标字符串 函数作用&#xff1a; 计算字符串的长度。 函数的模拟实现&#xff1a; size_t _strlen(const char * str) {ass…