计算代表n美分的方式的数量

时间:2015-11-29 09:02:51

标签: java algorithm

计算代表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));
        }
    }

}

2 个答案:

答案 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 = xamount % 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路,这是没有数字)。