备战秋招60天算法挑战,Day34

news2024/12/25 13:21:46

题目链接: https://leetcode.cn/problems/coin-change/

视频题解: https://www.bilibili.com/video/BV1qsvDeHEkg/

LeetCode 322.零钱兑换

题目描述

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

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

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

举个例子:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1

视频题解

零钱兑换

思路来源

思路来源

知识回顾

动态规划是一种通过将原问题分解为子问题来求解复杂问题的算法思想。它通常用于求解最优化问题,例如最长公共子序列、背包问题等。动态规划的核心思想是将原问题分解为若干个子问题,通过求解子问题的最优解推导出原问题的最优解。可以通过两点来判断一个问题能不能通过动态规划来解,一是该问题是否存在递归结构,二是对应的子问题能否记忆化。动态规划可以通过带备忘录的自上而下的递归自下而上的迭代来分别实现。由于递归需要用到栈来实现,一些语言对递归的深度是有限制的,所以自下而上的迭代是动态规划的最佳实现方式

思路解析

根据题意,零钱兑换的决策树如下图:

根节点表示要兑换的总金额amount,每个子节点表示amount已经兑换了coins[i](对于图中的例子0 <= i < 3)以后剩余要兑换的钱。叶子结点为0表示从根节点到叶子结点这条兑换路径是行得通的,叶子结点为负数表示行不通。

实际上本题就是要求在这棵树上所有行得通的兑换路径中,寻找最短合法路径的长度

整个决策树存在递归结构,还存在重复子问题两个节点2三个节点1,这些子问题计算一次后可以直接保存下来,避免多次重复计算。这就满足了使用动态规划的条件:存在递归结构子问题可以记忆化。所以本题可以用动态规划来解。动态规划可以通过带备忘录的自上而下的递归自下而上的迭代来分别实现。

方法一 自上而下的递归+备忘录

本题单纯的使用递归会超时的。

通过观察上图,可以看出每个分支存在很多相同的子问题,比如兑换总金额2元,兑换总金额1元。我们可以在递归的过程中使用备忘录把这些子问题的结果保存起来,后面就作为跳出递归的一个条件,这样可以大大节约时间。

C++代码

class Solution {
public:
  int coinChange(vector<int> &coins, int amount) {
    //定义备忘录
    vector<int> count(amount + 1, INT_MAX);
    count[0] = 0;
    int res = help(coins, count, amount);
    return res;
}

int help(vector<int> &coins, vector<int>& count, int amount) {
    //如果备忘录中已经保存结果
    if (count[amount] < INT_MAX - 1) {
        //直接返回
        return count[amount];
    }
    int coins_len = coins.size();
    int min_res = INT_MAX;
    for (int i = 0; i < coins_len; ++i) {
        if (amount - coins[i] >= 0) {
           //递归遍历(DFS)所以的可能性
            int res = help(coins, count, amount - coins[i]);
            if (res >= 0 && res < min_res) {
                min_res = res + 1;
            }
        }
    }
    //更新备忘录
    count[amount] = min_res == INT_MAX ? -1 : min_res;
    //返回结果
    return count[amount];
}

};

java代码

class Solution {
    public int coinChange(int[] coins, int amount) {
         // 定义备忘录
        int[] count = new int[amount + 1];
        Arrays.fill(count, Integer.MAX_VALUE);
        count[0] = 0;
        int res = help(coins, count, amount);
        return res;
    }

    private int help(int[] coins, int[] count, int amount) {
        // 如果备忘录中已经保存结果
        if (count[amount] < Integer.MAX_VALUE - 1) {
            // 直接返回
            return count[amount];
        }
        int min_res = Integer.MAX_VALUE;
        for (int coin : coins) {
            if (amount - coin >= 0) {
                // 递归遍历(DFS)所有的可能性
                int res = help(coins, count, amount - coin);
                if (res >= 0 && res < min_res) {
                    min_res = res + 1;
                }
            }
        }
        // 更新备忘录
        count[amount] = (min_res == Integer.MAX_VALUE) ? -1 : min_res;
        // 返回结果
        return count[amount];

    }
}

python代码

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 定义备忘录
        count = [float('inf')] * (amount + 1)
        count[0] = 0
        res = self.help(coins, count, amount)
        return res

    def help(self, coins, count, amount):
        # 如果备忘录中已经保存结果
        if count[amount] < float('inf') - 1:
            # 直接返回
            return count[amount]
        min_res = float('inf')
        for coin in coins:
            if amount - coin >= 0:
                # 递归遍历(DFS)所有的可能性
                res = self.help(coins, count, amount - coin)
                if res >= 0 and res < min_res:
                    min_res = res + 1
        # 更新备忘录
        count[amount] = -1 if min_res == float('inf') else min_res
        # 返回结果
        return count[amount]

复杂度分析

时间复杂度: 时间复杂度为O(mn),其中mcoins中元素的个数,n为要兑换的总金额。

空间复杂度: 空间复杂度为O(n)n为要兑换的总金额。

方法二 自下而上的迭代

实现自下而上而上迭代的两个关键步骤:确定状态转移公式边界情况

首先定义dp[i]表示兑换金额为i,使用coins中的零钱兑换所需最少的个数。dp[i]的准确定义是状态转移公式成功推导的关键。

针对coins = [1, 2, 5]amount = 4。从上面的决策树可以看出可以把兑换4块钱分解成兑换3元,兑换2元,兑换-1元,三个子问题。其中-1元是无法兑换的,可以直接把这个分支剪掉。可以得到下面的公式:

dp[4] = min{dp[3], dp[2]} + 1

类似地,把amount = 4扩展到amount = n,可以得到如下状态转移公式

dp[n] = min{dp[n-coins[0]], dp[n-coins[1]], ..., dp[n-coins[m-1]]} + 1

其中mcoins的大小,且需要n - coins[i] >= 00 <= i < m​。

接下来看一下边界情况,题目中已经说明amount = 0时对应的兑换个数为0,所以dp[0] = 0

C++代码

class Solution {
public:
  int coinChange(vector<int> &coins, int amount) {
    int coins_len = coins.size();
    //因为dp是从0开始,要兑换总金额为amount,所以要申请amount+1个元素
    vector<int> dp(amount + 1, amount + 1);
    //边界处理
    dp[0] = 0;
    for (int i = 1; i <= amount; ++i) {
      for (int j = 0; j < coins_len; ++j) {
        //剪掉节点为负的情况
        if (i - coins[j] >= 0) {
          //状态转移公式
          dp[i] = min(dp[i], dp[i-coins[j]] + 1);
        }
      }
    }
    return dp[amount] == amount + 1 ? -1: dp[amount];
  }
};

java代码

class Solution {
    public int coinChange(int[] coins, int amount) {
        int coins_len = coins.length;
        // 因为dp是从0开始,要兑换总金额为amount,所以要申请amount+1个元素
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        // 边界处理
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins_len; j++) {
                // 剪掉节点为负的情况
                if (i - coins[j] >= 0) {
                    // 状态转移公式
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] == amount + 1 ? -1 : dp[amount];
    }
}

python代码

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins_len = len(coins)
        # 因为dp是从0开始,要兑换总金额为amount,所以要申请amount+1个元素
        dp = [amount + 1] * (amount + 1)
        # 边界处理
        dp[0] = 0
        for i in range(1, amount + 1):
            for j in range(coins_len):
                # 剪掉节点为负的情况
                if i - coins[j] >= 0:
                    # 状态转移公式
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)
        return -1 if dp[amount] == amount + 1 else dp[amount]

复杂度分析

时间复杂度: 时间复杂度为O(m*n),其中mcoins中元素的个数,n为要兑换的总金额。

空间复杂度: 空间复杂度为O(n)n为要兑换的总金额。

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

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

相关文章

为数据仓库构建Zero-ETL无缝集成数据分析方案(上篇)

对于从事数据分析的小伙伴们来说&#xff0c;最头疼的莫过于数据处理的阶段。在我们将数据源的原始数据导入数据仓储进行分析之前&#xff0c;我们通常需要进行ETL流程对数据格式进行统一转换&#xff0c;这个流程需要分配专业数据工程师基于业务情况完成&#xff0c;整个过程十…

力扣每日1题--2181.合并零之间的节点

问题 下面我会向大家介绍我的思考过程和解题思路 解题思路 首先&#xff0c;我们看问题提供给我们的提示部分。第一点给了我们节点的数目&#xff0c;第二点给了我们val的范围&#xff0c;而我们这道题是要让我们求和的问题&#xff0c;那么我们就应该估算一下我们数据的一个…

多目标优化算法求解UF1、UF2、UF3、UF4、UF5、UF6、UF7、UF8、UF9、UF10

设计新的多目标测试函数时&#xff0c;需要考虑多个因素以确保它们能够有效地评估和比较多目标优化算法。以下是一些常见的设计原则和考虑因素&#xff1a; 目标函数的多样性&#xff1a;测试函数应涵盖不同类型的目标函数&#xff0c;如线性、非线性、凸函数、凹函数等&#x…

智慧矿山数字化工业大数据平台建设方案(52页PPT下载)

方案介绍&#xff1a; 传统矿山面临生产效率低、资源消耗大、安全隐患多、环境污染严重等问题&#xff0c;急需通过数字化转型实现可持续发展。而智慧矿山数字化工业大数据平台建设方案则可以有效的帮助矿业企业构建一个集数据采集、存储、处理、分析及应用于一体的智慧矿山数…

使用lspci命令获取加速卡型号

文章目录 前言一、lspci -nn 获取具体厂商及设备ID二、使用步骤三、使用3080Ti再查询一下 前言 新到的实验机器和加速卡&#xff0c;安装好之后发现lspci命令没有显示型号&#xff0c;这里记录下使用 Vendor ID和Device ID 通过网页查询获取加速卡具体型号的过程。 一、lspci …

Flutter动画—涟漪效果

功能分析 涟漪是由几个圆重叠在一起的 外层圆环比内层圆环的背景色要淡&#xff0c;可以改变外层圆的透明度 想要达到涟漪效果只要将每个圆的半径慢慢变大并且循环动画即可 ​实现方法 在画板上创建三个圆环&#xff0c;再实现外层的圆环要比内层圆环的颜色要淡。 class …

第二十四篇——地形篇:知己知彼知地形

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 战略层面的东西宏观了解了之后&#xff0c;越到最后&#xff0c;这些战术…

OpenAI gym: How to get complete list of ATARI environments

题意&#xff1a;OpenAI Gym&#xff1a;如何获取完整的 ATARI 环境列表 问题背景&#xff1a; I have installed OpenAI gym and the ATARI environments. I know that I can find all the ATARI games in the documentation but is there a way to do this in Python, witho…

神经网络—参数初始化、激活函数、损失函数及反向传播算法

基础知识点 神经网络NN(Neural Netwarks) 深度学习(Deep Learning) 神经元(Neuron) 深层神经网络&#xff08;Deep Neural Networks&#xff0c;DNNs&#xff09; 神经网络有下面三个基础层&#xff08;Layer&#xff09;构建而成&#xff1a; 输入层&#xff08;Input&am…

在线翻译百度,以及这三款实用便捷的翻译工具

嘿&#xff0c;朋友们&#xff0c;今天咱们来聊聊那些在日常生活和工作中不可或缺的在线翻译工具。说到这个&#xff0c;那绝对不能不提百度翻译了。那么&#xff0c;接下来就让我用口语化的方式给大家介绍一下它以及另外几款我推荐的翻译工具吧&#xff1a; 百度翻译推荐&…

CCF编程能力等级认证GESP—C++8级—20240907

CCF编程能力等级认证GESP—C1级真题 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)手套配对美丽路径 单选题&#xff08;每题 2 分&#xff0c;共 30 分&…

猿大师办公助手在线编辑Office/wps网页组件COM加载项启用说明

猿大师办公助手作为国内一款优秀的在线编辑Office插件&#xff0c;越来越受到更多客户的认可并实施了采购&#xff0c;猿大师办公助手与其他的厂商采用弹窗模式实现网页内嵌不同&#xff0c;猿大师办公助手是目前国内唯一真正实现网页内嵌本机Office的方案&#xff0c;效果如下…

怎样将vue项目 部署在ngixn的子目录下

如果同一服务器的80端口下,需要部署两个或以上数量的vue项目,那么就需要将其中一个vue项目部署在根目录下,其他的项目部署在子目录下. 像这样的配置 访问根目录 / 访问灭火器后台管理,访问 /mall/ 访问商城的后台管理 那么商场的vue项目,这样配置,才能在/mall/下正常访问? 1…

华为 HCIP-Datacom H12-821 题库 (10)

有需要题库的可以看主页置顶 V群进行学习交流 1.缺省情况下&#xff0c;BGP 对等体邻接关系的保持时间是多少秒&#xff1f; A、120 秒 B、60 秒 C、10 秒 D、180 秒 答案&#xff1a;D 解析&#xff1a; BGP 存活消息每隔 60 秒发一次&#xff0c;保持时间“180 秒” 2.缺省…

【Unity】简易而又实用的抽卡算法

1.两个数中任选一个&#xff08;抛硬币&#xff09; 基础版本&#xff1a; public int RandomBetweenTwoNumber(int a,int b) {float random Random.Range(0,1f);return radom<0.5f ? a : b ; } 升级版本&#xff08;支持概率调整&#xff09;&#xff1a; /*pa表示“…

MATLAB精美绘图详解

目录 一、常见二维图形绘制 二、常见三维图形绘制 三、图形修饰与处理 四、动画制作 五、常见绘图种类与特点总结 总结 MATLAB提供了非常丰富的绘图功能&#xff0c;不仅可以用于二维、三维的基本图形绘制&#xff0c;还包括特殊图形、动画制作等多种功能。在本文中&#…

Flutter框架——2.状态-路由-包-资源

文章参考了Flutter中国开源项目发起人杜文&#xff08;网名wendux&#xff09;创作的一本系统介绍Flutter技术的中文书籍《Flutter实战第二版》&#xff0c;网址&#xff1a;第二版序 | 《Flutter实战第二版》 https://book.flutterchina.club/#第二版变化 文章目录 一、状态管…

使用MATLAB进行动力学分析与可视化

目录 一、动力学与MATLAB概述 二、动力学系统的建模 1. 简谐振子 2. 单摆 三、动力学系统的仿真 1. 使用ode45求解简谐振子 2. 使用ode45求解单摆 四、动力学结果的可视化 1. 二维曲线图 2. 相空间图 3. 三维曲面图 4. 动画制作 五、复杂动力学系统的建模与仿真 1…

3、Hadoop部署

1、 Hadoop部署 1&#xff09;集群部署规划 注意&#xff1a;NameNode和SecondaryNameNode不要安装在同一台服务器 注意&#xff1a;ResourceManager也很消耗内存&#xff0c;不要和NameNode、SecondaryNameNode配置在同一台机器上。 hadoop102 hadoop103 hadoop104 HDFS…

SSM框架学习(二:SpringFramework实战指南)

目录 一、SpringFramework介绍 1.总体技术体系 &#xff08;1&#xff09;单一架构 &#xff08;2&#xff09; 分布式架构 2.Spring 和 SpringFramework概念 &#xff08;1&#xff09;广义的 Spring&#xff1a;Spring 技术栈&#xff08;全家桶&#xff09; &#xff…