数据结构与算法-26.零钱兑换

26、零钱兑换

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qRhb6mUQ-1637919358632)(image-20211126171934249.png)]

26.0、记忆化搜索

其实就是暴力解法,比如对于金额20,硬币面值为【1,2,5】

看看15需要多少硬币,再看看10需要多少硬币,再看看5需要多少硬币。算出需要4枚

看看18需要多少硬币,再看看13需要多少硬币,再看看8需要多少硬币,再看看3需要多少硬币,再看看1需要多少硬币。算出6枚

看看19需要多少硬币,再看看14需要多少硬币,再看看9需要多少硬币,再看看4需要多少硬币,再看看2需要多少硬币。算出6枚

所以结果是4枚。

上述例子可以正好凑够金额,所以没有分支,如果金额是20,硬币面值是【2,5,7】呢

看看13需要多少硬币,再看看6需要多少硬币,再看看1需要多少硬币。在这里出现分支,因为1不能凑够,所以需要回退

-------------------------------------------------------------,再看看4需要多少硬币,再看看2需要多少硬币。算出5枚

其实这种方法展开来看就是一个树,这个树把所有情况都包括进去了,也就是暴力了

这种做法其实是超时的,但是可以用一个数组来记录一些面值所需要的硬币数,来减少时间开销,但是还不如直接使用下面的动态规划来得简单,因为这样的话本质上就和动态规划一样了,只不过把迭代变成了递归。

时间复杂度:O(S n)S指的是金额,n指的是硬币的种类

空间复杂度:O(S)需要S大小的空间来优化原本的暴力

26.1、动态规划

我们来设F(x) = x的金额最少需要硬币的数量,硬币序列为【1,2,5】

那状态转移方程就是F(x) = min【F(x-1),F(x-2),F(x-5)】+1

哦了,就这。

//这是俺的垃圾解法
int coinChange(vector<int>& coins, int amount) 
{
    if (!amount)return 0;
    vector<int> data(amount + 1, -1);//创建大小为amount+1的数组
    for (auto& i : coins)
        if (i <= amount)data[i] = 1;//将每一硬币面值的对应的下标的值都记为1

    for (int i = 1; i <= amount; i++)
    {
        if (data[i] == 1)continue;
        int preMinWays = INT_MAX;

        for (auto& j : coins)
            if (j < i && data[i - j] != -1)preMinWays = min(preMinWays, data[i - j]);
        if (preMinWays != INT_MAX)
            data[i] = preMinWays + 1;
    }
    return data[amount];
}

时间复杂度:O(S n)S指的是金额,n指的是硬币的种类

空间复杂度:O(S)需要S大小的空间来进行记录

下面是代码的优化

//俺滴垃圾代码的优化
int coinChange2(vector<int>& coins, int amount)
{
    vector<int> data(amount + 1, amount + 1);
    data[0] = 0;
    for (int i = 1; i <= amount; i++)
        for (auto& j : coins)
            if (j <= i)data[i] = min(data[i], data[i - j] + 1);
    return data[amount] > amount ? -1 : data[amount];
}

性能是不变的,但是在实际的测试时候,下面这个总是慢一点(俺也不知道是为什么)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值