零钱兑换
给你一个整数数组 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(我能优化的最快速度了)