该问题希望用户返回一个最小硬币列表作为找零。例如[.01,.10,.25],.40。并且(所有硬币都有10个补给)应返回[.10,.10,.10,.10],但不返回[.25,.1,.01,.01,.01,.01,.01] < / p>
贪婪的方法行不通。此问题是动态编程问题。所描述的解决方案是O(2 ^ n)。自底向上方法如何将其优化为O(n ^ 2)或更佳?
class CoinChange {
public static List<Double> findMinRefundCombination(List<Double> inputCoins, double refundToMake) {
List<Double> minCoins = new ArrayList<>();
List<Double> coinsAccumulatedSoFar = new ArrayList<>();
double refundSoFar = 0.0d;
findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins,coinsAccumulatedSoFar, 0, refundSoFar);
System.out.println(minCoins.size());
return minCoins;
}
public static void findMinRefundCombinationHelper(List<Double> inputCoins, double refundToMake, List<Double> minCoins, List<Double> coinsAccumulatedSoFar, int curIndex, double refundSoFar) {
if(refundSoFar > refundToMake || curIndex == inputCoins.size()) {
return;
}
if(refundSoFar == refundToMake) {
if(minCoins.isEmpty()) {
for(Double coin: coinsAccumulatedSoFar)
minCoins.add(coin);
} else {
if(coinsAccumulatedSoFar.size() < minCoins.size()) {
minCoins.clear();
for(Double coin: coinsAccumulatedSoFar)
minCoins.add(coin);
}
}
}
coinsAccumulatedSoFar.add(inputCoins.get(curIndex));
// findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar,curIndex,refundSoFar + inputCoins.get(curIndex));
findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar, curIndex + 1, refundSoFar + inputCoins.get(curIndex));
coinsAccumulatedSoFar.remove(coinsAccumulatedSoFar.size() - 1);
findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar, curIndex + 1, refundSoFar);
}
public static void main(String[] args) {
List<Double> inputCoins = new ArrayList<>();
inputCoins.add(.01);
// inputCoins.add();
inputCoins.add(.10);
inputCoins.add(.25);
inputCoins.add(0.50);
inputCoins.add(1.0);
double refundToMake = 0.40;
List<Double> minCoins = findMinRefundCombination(inputCoins, refundToMake);
for(Double coin: minCoins)
System.out.print(coin + " ");
System.out.println();
}
}
答案 0 :(得分:1)
如果您要表示的数量足够少,则此问题可以转换为背负式背包。
在注释中,您声明所有数字的精度是两位小数,因此所有数字都可以通过将它们乘以100来转换为整数。让我们从原始输入中给定的每个硬币中创建10个硬币,然后声明我们最多可以使用每个新硬币一次。
这里的想法类似于背包(Knapsack):让我们介绍一个函数F(k, i)
,该函数表示如果我们仅使用前几个k
硬币,则需要多少硬币来实现总和i
。例如,F(0, i)
为0,因为对于我们可用的任何硬币子集,我们都可以不使用它们而获得总和0。 F(k > 0, 0)
是未定义的,因为我们不能不使用硬币就无法获得大于0的总和,并且F(|value of the first coin|, 1)
等于1。请注意,F(k, 10N)
将是解决问题的方法。
对于F(k, i) = min(F(k, i - 1), F(k - |value of coin i|, i - 1))
的适用值,此处的递归关系为k, i
。用英语来说,我们是在说“要么我们使用第i个硬币,在这种情况下,总和必须增加其价值,否则我们就没有”。