力扣(322)----------零钱兑换

题目描述:

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

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

示例 2:
输入: coins = [2], amount = 3
输出: -1

分析:

首先明确条件,每一个硬币是可以多次使用的,通过选择来达到最优解。其实这个问题可以转换成那种爬楼梯的问题,这样的话更容易理解 如:我要达到amout层阶梯。我每次走一次的选择有(coins {1,2,5})。那么我最少需要走多少次呢。那么这个就涉及每一步的取舍问题。

解法一:dfs递归
我们可以 使用循环递归的方式:递归选择coins{1,2,5}。这里我们可以做一个技术处理 就是将coins进行排序,然后从最大的开始。(条件是剩余可走的台阶数量大于等于,我们这次所选择的走法。)。然后每次递归时,就可以 选或者不选,这样递归下去。比如{1,2,5}中,选5进入递归、不选进入一次递归。然后每次新进入的递归。都有两种选择,选择当走法,不选择当走法。 然后当正好走完时,记录走的次数。取出最小的即可。具体的代码也就不展示了,思路明白即可。

解法二:动态规划
定义DP[i]:表示,当凑出总金额为i的时候,所需要的最小银币数量(或者:当需要到低i个台阶上时,所需最小走的次数)。
那么动态转移方程DP[i]该如何表示呢?。是不是应该等于min{ DP[i] - coins[ j ]} + 1 ,也就是上一次选择之前的最小DP[i] 加上所选的这次

package leetcode;

import org.junit.Test;

import java.util.Arrays;

/**
 * @author liuzihao
 * @create 2019/12/22-21:38
 * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
 * 如果没有任何一种硬币组合能组成总金额,返回 -1。
 */
public class Demo322 {
    @Test
    public void test(){
        //得出动态方程定义DP[i]:总金额为amout时的最小coins数量
        //结果集合 其实也就是动态方程中的一个解
        // J: 0-N coins[n]
        // DP[I] = MIN { DP[I -COINS[J]] }从而可以推导出
        int [] coins = {1, 2, 5};
        int amount = 11;
        System.out.println(coinChange(coins,amount));
    }
    // 输入: coins = [1, 2, 5], amount = 11
    public int coinChange(int[] coins, int amount) {
        int [] dp = new int[amount+1];
        Arrays.fill(dp,1,dp.length,amount+1);//设置默认值,用于最后检验是否有解
        dp[0] = 0; //初始化0 表示当需要0个总数的时候 有0总解法
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++){
                if (i-coins[j] >= 0){
                    dp[i] = Math.min(dp[i-coins[j]] + 1 ,dp[i]);
                }
            }
        }
        // 当amount 为 amount+1 的时候 , 也就是没有赋值 此时无解 返回-1 
        return dp[amount]>amount ? -1:dp[amount];
    }
}

发布了98 篇原创文章 · 获赞 44 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43732955/article/details/103661568
今日推荐