322. 零钱兑换 (三种方法,逐步优化时间复杂度)- 力扣(LeetCode)

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

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

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

示例 1:

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

题解

同一个题目的不同种解法

使用 Leetcode 组合总和 的思路

因为每个硬币都可以选无数次,那么我们可以使用固定的顺序选择硬币
遇到这一类相同元素不计算顺序的问题,我们在搜索的时候就需要 按某种顺序搜索。具体的做法是:每一次搜索的时候设置 下一轮搜索的起点 begin
加粗样式
从每一层的第 2 个结点开始,都不能再搜索产生同一层结点已经使用过的 硬币 里的元素
参考文章:https://leetcode.cn/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/

class Solution {
    public int coinChange(int[] coins, int amount) {
        mem = new int[coins.length+1][amount+1];
        for(int i=0; i<coins.length; ++i)
            Arrays.fill(mem[i],-1);
        int res = dfs(0,amount,coins);
        return res == 10001 ? -1 : res;
    }
    int [][] mem;
    int dfs(int idx,int amount,int []coins){
        if(amount==0){
            return 0;
        }
        if(idx>=coins.length){
            return 0;
        }
        if(mem[idx][amount]!=-1) return mem[idx][amount];  //记忆化搜索

        int ans = 10001;
        for(int i=idx; i<coins.length; ++i){
            if(amount-coins[i]>=0){
                ans = Math.min(ans, dfs(i, amount-coins[i], coins)+1);
            }
            
        }
        // System.out.println(ans);
        mem[idx][amount] = ans;
        return ans;
    }
}

虽然能AC但是效果不甚理想,只击败了5%,执行用时58 ms

完全背包的思想

选或不选,并且拿一个记忆化数组记忆重复的计算

class Solution {
    public int coinChange(int[] coins, int amount) {
        mem = new int[coins.length+1][amount+1];
        for(int i=0; i<coins.length; ++i)
            Arrays.fill(mem[i],-1);
        int res = dfs(0,amount,coins);
        return res == 10001 ? -1 : res;
    }
    int [][] mem;
    int dfs(int idx,int amount,int []coins){
        if(amount==0){
            return 0;
        }
        if(idx>=coins.length){
            return 10001;
        }
        if(mem[idx][amount]!=-1) return mem[idx][amount];  //记忆化搜索

        int ans = 10001;
        ans = Math.min(ans, dfs(idx+1,amount,coins));  //选或不选
        if(amount-coins[idx]>=0){
            ans = Math.min(ans, dfs(idx,amount-coins[idx],coins)+1);
        }
        mem[idx][amount] = ans;
        return ans;
    }
}

击败了9%,执行用时 37 ms(优化了一点点)

不考虑重复的路径,直接选就完事,并且用记忆化优化

思路是每次都把每个硬币可能的选择选一遍,并记录剩下的钱数可以组成总金额的最小硬币数

class Solution {
    public int coinChange(int[] coins, int amount) {
        mem  = new int[amount+1];
        Arrays.fill(mem,-1);

        int res = dfs(amount, coins);
        return res == 10001 ? -1 : res;
    }
    int mem[];

    int dfs(int rest, int []coins){
        if(rest==0) return 0;
        if(mem[rest]!=-1) return mem[rest];

        int ans = 10001;
        for(int coin : coins){
            if(rest-coin>=0){
                ans = Math.min(dfs(rest-coin, coins)+1,ans);
                
            }
        }
        mem[rest] = ans;
        return ans;
    }
}

击败20%,执行用时21ms(我能优化的最快速度了)
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葛济维的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值