最优硬币组合问题

96 阅读2分钟

问题描述

小C有多种不同面值的硬币,每种硬币的数量是无限的。他希望知道,如何使用最少数量的硬币,凑出给定的总金额N。小C对硬币的组合方式很感兴趣,但他更希望在满足总金额的同时,使用的硬币数量尽可能少。
例如:小C有三种硬币,面值分别为 125。他需要凑出总金额为 18。一种最优的方案是使用三个 5 面值的硬币,一个 2 面值的硬币和一个 1 面值的硬币,总共五个硬币。

思路分析

这道题的难度为中等难度,是一道典型的背包问题。传入的参数为一个包含硬币类型的int数组和期望得到的目标和,我们的方法需要返回一个包含记录最少硬币组合的List。

首先我们创建一个一个二维dp List用于记录我们动态规划中的组合。dp第一个维度范围为[0, taotal]表示金额总数的组合,第二个维度为硬币使用情况。我们创建名为dpCount的List数组记录每个金额额度的最小金币数。
对于传入数组进行for-each循环访问,即对于每个可用硬币值num,将其与dp中的相加,若dp[i - num]<dp[i]就是说明产生了额度为i的更少硬币数量组合,我们基于ArrayMap的clone方法将dp[i - m]引用的Map复制到dp[i],并将num加入dp[i]。
基于以上循环步骤,我们得到的dp[total]引用的List对象为总额度为total且使用硬币数量最少的组合,由于测试用例均为逆序排序,因此我们最后基于lambda定义逆序排序对该List进行排序得到最后输出的List。

image.png

模拟测试用例

以第二个测试用例coins = [1, 3, 4], amount = 6模拟程序走向
1、num=1, dp=[[], [], [], [], [], [], []], dpCount=[0, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE]
2、num=3, dp=[[], [1], [1, 1], [3], [1, 3], [1, 1, 3], [3, 3]], dpCount=[0, 1, 2, 1, 2, 3, 2]
3、num=4, dp=[[], [1], [1, 1], [3], [4], [1, 4], [3, 3]], dpCount=[0, 1, 2, 1, 1, 2, 2]
最后得到dp[4]=[3, 3]作为答案输出

总结

常见的dp背包问题往往求最小值或最大值数量,因此可以用一维Array记录dp;本题还需要输出最少的组合因此需要多一个二维List记录最少的组合情况。测试用例和提交与答案因为顺序不同而出错,因此最后使用了排序。

`