计算代表n美分的方式数量的算法难题,使用25美分,10美分,5美分和1美分的无限数量。提到了几个不同的解决方案并对下面的解决方案有疑问,我不确定为什么我们有信心if (index >= denoms.length - 1)
必须有解决方案,并且必须有一个独特的解决方案?它是一种特殊的优化,因为我们有一个= 1的特殊值,或者更通用的优化方法?欣赏更多见解。
import java.util.Arrays;
public class Question {
public static int makeChange(int amount, int[] denoms, int index) {
if (index >= denoms.length - 1) return 1; // one denom remaining -> one way to do it
int denomAmount = denoms[index];
int ways = 0;
for (int i = 0; i * denomAmount <= amount; i++) {
int amountRemaining = amount - i * denomAmount;
ways += makeChange(amountRemaining, denoms, index + 1); // go to next denom
}
return ways;
}
public static int makeChange1(int n) {
int[] denoms = {25, 10, 5, 1};
return makeChange(n, denoms, 0);
}
public static int makeChange2(int n) {
int[] denoms = {25, 10, 5, 1};
int[][] map = new int[n + 1][denoms.length];
return makeChange2(n, denoms, 0, map);
}
public static int makeChange2(int amount, int[] denoms, int index, int[][] map) {
if (map[amount][index] > 0) { // retrieve value
return map[amount][index];
}
if (index >= denoms.length - 1) return 1; // one denom remaining -> one way to do it
int denomAmount = denoms[index];
int ways = 0;
for (int i = 0; i * denomAmount <= amount; i++) {
// go to next denom, assuming i coins of denomAmount
int amountRemaining = amount - i * denomAmount;
ways += makeChange2(amountRemaining, denoms, index + 1, map);
}
map[amount][index] = ways;
return ways;
}
public static int makeChange(int n) {
int x = makeChange1(n);
int y = makeChange2(n);
if (x != y) {
System.out.println("Error: " + x + " " + y);
}
return x;
}
public static void main(String[] args) {
for (int i = 0; i <= 100; i++) {
System.out.println("makeChange(" + i + ") = " + makeChange(i));
}
}
}
答案 0 :(得分:1)
如果在index > denoms.length - 1
时没有返回,则denoms[index]
会提供ArrayIndexOutOfBoundsException
。
现在问题是为什么return 1
时会index == denoms.length - 1
?
原因很简单,因为最后一个名称只有1美分!
任何amountRemaining
只有一种方法可以做1美分。
因此,如果您将数组更改为{25, 10, 5, 2}
,您会发现您的解决方案已被细分(当涉及到{25, 10, 5, 2}
时,makeChange(3)
将返回1
,但是您看,它应该是0
)。
如果您想要在最后一个denom不是1美分时修复您的解决方案,您只需将if (index >= denoms.length - 1) return 1
更改为:
if (index > denoms.length - 1)
return 0;
if (index == denoms.length - 1) {
// one denom remaining
if (amount % denoms[denoms.length - 1] == 0) {
return 1;
}
else {
return 0;
}
}
然后使用{25, 10, 5, 2}
:
makeChange(1) = 0
makeChange(2) = 1
makeChange(3) = 0
编辑:
这是一个特殊的优化,因为我们有一分= 1特殊 价值,或更一般的优化方法?
您可以将其称为优化,但正如我的代码所示,您可以使用任何cent = x
,amount % denoms[denoms.length - 1] == 0
进行优化。
答案 1 :(得分:1)
如果我没弄错的话,这非常coin changing problem。
我认为您理解这个问题,因为您的问题非常具体针对该行if (index >= denoms.length - 1) return 1;
,所以我将跳过解释。
有趣的是,我使用最小面额分别为1,2,3和4美分在Eclipse中运行您的整个代码[I.E. 25/10/5/1,或25/10/5/2,或25/10/5/3,或25/10/5/4]。在每一次运行中,我都得到了完全相同的解决方案(这些代表了最小的子问题)。那么为什么会这样呢?如果我有(例如)3美分,那么25/10/5/4的解决方案应该是0因为我不能拿一枚硬币(因为最小面额大于金额)是不是有意义的需要什么?但是,如果您认为它返回的方式没有找到解决方案,那该怎么办呢?也就是说,如果1回报代表空的硬币集合,其中硬币未达到所需金额(或没有硬币)?
在问题(n = 6)中花费6美分,面额为25/10/5/4,我们得到5 +无[无效],4 +无[无效]的解。如果最小面额是3,那么我们得到5 +没有[无效]和3 + 3。重复最小2美分,我们得到5 +没有[无效],2 + 2 + 2.重复最小1分,我们得到5 + 1和1 + 1 ...... + 1.每个解决方案的总数最终尽管案件无效,仍然是2岁!
这是因为您提供的解决方案采用无效方式(但是当硬币的值大于最小面额时采用硬币和空集)计为每种方式。这就是为什么即使当n = 10(2是最小面额)时,我们得到4种方式(10,5 * 2,2 * 5和5 + 2 * 2 [无效])。请注意,无效案例是“最佳解决方案”,这意味着这些是与每个面额相关联的“最佳”无效案例。
我确实认为Sayakiss提供的解决方案可以消除这种无效方式被计算的问题(特殊情况是n = 0返回1路,这是没有数字)。