332.零钱兑换

题目描述

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

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

输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。


解法一:动态规划

  • 符合[最优子结构] 求amount=11 凑出amount=10的最少硬币数再选面值1的硬币就是原问题答案,硬币数没限制,所以子问题相互独立

如何列出状态转移方程

  • 先确定[状态] ,就是原问题和子问题中变化的变量,由于硬币数量无限,唯一状态是目标金额amount
  • 然后确定dp函数的定义: 当前目标金额是n 至少需要dp(n)个硬币凑出该金额
  • 然后确定[选择]并择优: 做出什么选择会改变当前状态,在这个问题就是选择一枚硬币,目标金额减少
  • 最后明确 base case: 目标金额为0,所需硬币数量为0;目标金额小于0 无解返回-1
int count=0;
    public int coinChange(int[] coins,int amount) {
        //base case
        if(amount==0){
            return 0;
        }
        if(amount<0){
            return -1;
        }
        int res=Integer.MAX_VALUE;
        //暴力递归
        for(int coin:coins){
            count=coinChange(coins,amount-coin);
            if(count==-1){
                continue;
            }
            res=Math.min(res,1+count);
        }
        return res!=Integer.MAX_VALUE?res:-1;
    }

在这里插入图片描述
状态方程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcrN59YL-1584806651619)(en-resource://database/8847:1)]
递归树
在这里插入图片描述
时间复杂度
O(n^k)

带备忘录的递归
  • 备忘录记录当前金额数记录过的计算金额数最少次数
class Solution {
    int count=0;
    public int coinChange(int[] coins,int amount) {
        int[] memo =new int[amount+1];
        return helper(coins,amount,memo);
    }
    public int helper(int[] coins,int amount,int[] memo){
        if(amount==0){
            return 0;
        }
        if(amount<0){
            return -1;
        }
        if(memo[amount]!=0){
            return memo[amount];
        }
        int res=Integer.MAX_VALUE;
        for(int coin:coins){
            count=helper(coins,amount-coin,memo);
            if(count==-1){
                continue;
            }
            res=Math.min(res,1+count);
        }
        memo[amount]=res!=Integer.MAX_VALUE?res:-1;
        return memo[amount];
    }
}

在这里插入图片描述

dp数组的迭代解法

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] memo = new int[amount+1];
        Arrays.fill(memo,amount+1);
        //别忘了初始化
        memo[0]=0;
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.length;j++){
                if(i>=coins[j]){
                    memo[i]=Math.min(memo[i],memo[i-coins[j]]+1);
                }
            }
        }
        return memo[amount]>amount?-1:memo[amount];
    }
}

方法二: 贪心+回溯

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LIfI5P98-1584806651626)(en-resource://database/8859:1)]

class Solution {
    //ans答案用全局方便点,不用考虑该变量一直变换回不来的问题
    int ans=Integer.MAX_VALUE;
    public int coinChange(int[] coins, int amount) {
        if(amount==0){
            return 0;
        }
        Arrays.sort(coins);
        dfs(coins,amount,coins.length-1,0);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
    public void dfs(int[] coins,int amount,int index,int count){
        //找到后会继续搜索,看有没有其他的更小的count
        if(amount==0){
            ans=Math.min(ans,count);
            return;
        }
        if(index<0){
            return;
        }
        //有k+count<ans,保证超过ans的不继续计算,实现剪枝
        for(int k=amount/coins[index];k>=0&&k+count<ans;k--){
            //深入找,找到了返回,再尝试其他值
            dfs(coins,amount-k*coins[index],index-1,count+k);
        }
    }
}

扩展

  • 写了一个数组最少金额的组合数的代码
package findMoney;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
class Solution {
    List<Integer> list;
    List<Integer> temp;
    int ans=Integer.MAX_VALUE;
    boolean flag;
    public int coinChange(int[] coins, int amount) {
        list=new ArrayList<>();
        temp=new ArrayList<>();
        if(amount==0){
            return 0;
        }
        dfs(coins,amount,coins.length-1,0);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
    public void dfs(int[] coins,int amount,int index,int count){
        if(amount==0){
            if (count<ans){
                ans=count;
                list=new ArrayList<>(temp);
            }
            return;
        }
        if(index<0){
            return;
        }
        for(int k=amount/coins[index];k>=0&&k+count<ans;k--){
            flag=false;
            if(flag){

                temp.add(coins[index]);
            }
            else{
                for(int i=0;i<k;i++) {
                    temp.add(coins[index]);
                    flag = true;
                }
            }
            dfs(coins,amount-k*coins[index],index-1,count+k);
        }
    }
    public static void main(String[] args) {
        int[] nums = {1,2,5,10,50};
        Random random=new Random();
        Solution solu = new Solution();
        int sumAmount=random.nextInt(100);
        int paid=random.nextInt(100)%(100-sumAmount+1)+sumAmount;
        int back=paid-sumAmount;
        System.out.println(sumAmount);
        System.out.println(paid);
        System.out.println(back);
        System.out.println(solu.coinChange(nums,back));
        System.out.println(solu.list);
    }
}
发布了13 篇原创文章 · 获赞 0 · 访问量 306

猜你喜欢

转载自blog.csdn.net/m0_37605197/article/details/105020547