首页 > 编程语言 >[牛客BM70&LeetCode322]零钱兑换Ⅰ——DFS,记忆化搜索,动态规划(C++)

[牛客BM70&LeetCode322]零钱兑换Ⅰ——DFS,记忆化搜索,动态规划(C++)

时间:2023-03-15 21:23:07浏览次数:38  
标签:arr 硬币 int min C++ aim 牛客 depth DFS

题目描述

给你一个整数数组arr,表示不同面额的硬币;以及一个整数aim,表示需要放入钱包的目标金额。
计算并返回可以凑成总金额所需的最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回-1 。
每种硬币的数量无限。

  • 用例1:
    输入:[1, 2, 3], 6
    输出:2(即3+3)

思路一:深度优先搜索

本题自然可以通过遍历所有可能的硬币组合以求得最少的硬币数量。
每次都选择三种面额(以用例1举例)中的一枚放入到钱包中,直到钱包达到目标金额。

上面这个思路其实就是深度优先搜索的方法(DFS)。递归深度就是使用的硬币的个数。
然而这种方式将会出现大量的重复计算,比如用例中:6-2=4,6-1-1=4;导致4这个节点会被多次计算。

如图


值为4的节点和值为3的节点都多次出现。

参考代码如下:

class Solution {
private:
    int _min_depth = INT_MAX;  // 初始化递归深度为(近似)无限大
public:
    void dfs(vector<int> &arr, int rem, int depth) {
        if (rem == 0) {
            _min_depth = min(_min_depth, depth);  // 每次递归到结束条件就更新最小的深度
            return ;
        }

        for (auto coin : arr)
            dfs(arr, rem - coin, depth + 1);  // 每次递归深度+1
    }
    int minMoney(vector<int>& arr, int aim) {
        dfs(arr, aim, 0);
        return _min_depth;
    }
};

思路二:记忆化搜索

根据上文分析,可以将已经出现过的钱包剩余目标金额的最少硬币数记录下来,应该使用数组记录1 ~ aim范围内所有的金额的最少硬币数。

class Solution {
public:
    int dfs(vector<int> &arr, vector<int> &mem, int aim) {
        if (aim < 0) return -1;
        if (aim == 0) return 0;
        if (mem[aim] <= aim) return mem[aim];
        for (int i = 0; i < arr.size(); ++i) {
            int temp = dfs(arr, mem, aim-arr[i]);
            if (temp != -1)
                mem[aim] = min(mem[aim], temp + 1);    
        }

        if (mem[aim] <= aim)
            return mem[aim];
        else
            return mem[aim] = -1;
    }
    int minMoney(vector<int>& arr, int aim) {
        vector<int> mem(aim + 1, aim + 1); // 初始化数组
        return dfs(arr, mem, aim);
    }
};

思路三:动态规划

深度优先搜索采用自上而下的方法,“长”成了一棵树。
动态规划则是自下而上,找到每个节点的最小硬币数,不会是一个树状结构。

class Solution {
public:
    int minMoney(vector<int>& arr, int aim) {
        vector<int> dp(aim + 1, aim + 1);
        dp[0] = 0;
        for (int i = 1; i <= aim; ++i) {
            for (int j : arr) {
                if (i >= j) 
                    dp[i] = min(dp[i], dp[i - j] + 1);
            }
        }        
        
        if (dp[aim] != aim + 1) return dp[aim];
        else return -1;
    }
};

错误的思路:贪心+DFS

有种错误的思路是,认为放尽量多的面额大的硬币就可以让使用硬币的数量最少(即贪心思想)。如果每种面额的硬币只有一枚,那么可以使用贪心思想。
然而,考虑这种情况:如目标金额10,硬币为7,5,1;则最少硬币数是2(5+5)而不是4(7+1+1+1)。
这里也提供错误思路的代码:

class Solution {
  private:
    int _min_depth = INT_MAX;
    int flag = false;
  public:
    void dfs(vector<int>& arr, int rem, int depth) {
        if (rem == 0) {
            flag = true;
            _min_depth = depth;
            return ;
        }

        for (auto coin : arr) {
            if (!flag) dfs(arr, rem - coin, depth + 1);
        }
    }
    int minMoney(vector<int>& arr, int aim) {
        sort(arr.begin(), arr.end(), greater<>());
        dfs(arr, aim, 0);
        return _min_depth == INT_MAX ? -1 : _min_depth;
    }
};

标签:arr,硬币,int,min,C++,aim,牛客,depth,DFS
From: https://www.cnblogs.com/yang5sui/p/17210780.html

相关文章

  • 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 05.认识GameMode&自动生成AI角色
    斯坦福课程UE4C++ActionRoguelike游戏实例教程0.绪论概述本篇文章将会讲述UE中Gamemode的基本概念,并在C++中开发GameMode,为游戏设置一个简单的玩法:使用环境查询自动......
  • 「AcWing学习记录」DFS
    AcWing842.排列数字原题链接#include<iostream>usingnamespacestd;constintN=10;intn;intpath[N];boolst[N];voiddfs(intu){if(u==n)......
  • 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 04.角色感知组件PawnSensingComponent
    斯坦福课程UE4C++ActionRoguelike游戏实例教程0.绪论概述本文章对应课程第十一章43、44节。本文讲述PawnSensingComponent中的视觉感知的使用,以及对AI角色平滑转身......
  • C++学习记录
    C++recordnotebook基础导论C++特性具有c访问硬件的能力和面向对象程序的属性,以及更具有泛型编程的功能(使用模板进行编程)。OOP(面向对象编程)其中的方法有:自顶向下和......
  • C++ 构造函数和析构函数
    构造函数和析构函数目录页面问题构造函数与析构函数初始化列表转换构造拷贝构造(这种都是浅拷贝,每一项成员依次拷贝过去)默认的赋值运算符小的总结页面构造/......
  • C++ 常用语法
    1.定义一个字符串常量staticconststd::stringversion("0.0.1");staticconststd::stringname("Car-"+version);2.定义size大小staticconstexpruint64_tsh......
  • C++风格 字符串操作
    获取字符串长度              str.size();或者str.length();连接字符串                     str=str+"world";删除字符串......
  • [计算机基础笔记] C/C++
    C语言面向过程,C++面向对象。面相过程的思维方式,它更加注重这个事情的每一个步骤以及顺序。他比较直接高效,需要做什么可以直接开始干。程序=算法+数据面向对象的思维方式......
  • C++学习笔记3
    18.虚析构问题提出:在继承关系中构造和析构什么时候被调用?假如当前有类CSon继承CFather构造:当newCSon的时候,就会调用CSon(),程序跳进CSon(),在CSon()里会先调用CFather(......
  • C++学习笔记4
    C++=C+面向对象+泛型编程+STL26.STL容器STL(标准模板库),它其中包含了:容器、迭代器、算法、空间配置器、配接器、仿函数六个部分,这里介绍一些容器以及几个简单算法......