Java 求解零钱兑换

Posted 南淮北安

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 求解零钱兑换相关的知识,希望对你有一定的参考价值。

一、题目

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

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

二、完全背包解析

题目要求使用硬币凑总金额,每枚硬币可以使用多次,要求硬币个数最少,所以是完全背包问题,硬币个数最少是组合问题

(1)确定dp数组及下标含义

dp[j] 表示:总金额为 j 时,硬币最少是多少枚

(2)确定递推表达式

对于硬币 coins[i] 存在两种情况,要么放,要么不放,所以有:dp[j],dp[j-coins[i]]+1
所以要求硬币最少的情况为:Math.min(dp[j],dp[j-coins[i]]+1);

这里之所以执行加1操作,是因为这里是求得是硬币数目,如果是求组合个数则不加1,或者也可以认为和初始化时的情况有关

(3)确定初始化情况

dp[0]=0;表示当总金额为0时,需要0枚硬币
同时对于非零的情况初始化为无穷大,因为此时求最小的,不然会被覆盖,同理如果求最大的情况,则初始非零为0

int max = Integer.MAX_VALUE;
//初始化dp数组为最大值
for (int j = 0; j < dp.length; j++) 
    dp[j] = max;

//当金额为0时需要的硬币数目为0
dp[0] = 0;

(4)确定遍历顺序

本题是求组合的情况,所以可以先遍历物品,再遍历总金额,因为完全背包问题,所以内层循环正序遍历

三、代码

class Solution 
    public int coinChange(int[] coins, int amount) 
        int max = Integer.MAX_VALUE;
        int[] dp = new int[amount + 1];
        //初始化dp数组为最大值
        for (int j = 0; j < dp.length; j++) 
            dp[j] = max;
        
        //当金额为0时需要的硬币数目为0
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) 
            //正序遍历:完全背包每个硬币可以选择多次
            for (int j = coins[i]; j <= amount; j++) 
                //只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要
                if (dp[j - coins[i]] != max) 
                    //选择硬币数目最小的情况
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                
            
        
        return dp[amount] == max ? -1 : dp[amount];
    

四、总结

当前情况,可以由上一步的选择推得,所以选择动态规划

动态规划四部曲

每个硬币可以选择多次,所以属于完全背包问题

求组合,对遍历顺序没有要求

由于求组合中硬币最少为多少,所以初始化时非零位初始最大,由于此时初始化为最大,所以这里进行具体选择最小判断时,需要加个if语句判断下,不然会造成越界出现错误

if (dp[j - coins[i]] != max) 
	//选择硬币数目最小的情况
	dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);

以上是关于Java 求解零钱兑换的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode.322-零钱兑换(最大/小型动态规划)

Leetcode.322-零钱兑换(最大/小型动态规划)

动态规划:零钱兑换问题(javascript求解)

数据结构与算法之深入解析“零钱兑换”的求解思路与算法示例

java刷题--322零钱兑换

322. 零钱兑换