繁体   English   中英

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

[英]calculating the number of ways of representing n cents

使用不限数量的25美分,10美分,5美分和1美分来研究计算n美分的方式数量的算法难题。 提到了几种不同的解决方案,并对下面的解决方案有疑问,我不确定为什么我们有把握if (index >= denoms.length - 1)必须有解决方案,并且必须有唯一的解决方案? 是仅仅因为我们有cent = 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));
        }
    }

}

如果在index > denoms.length - 1时不返回,则denoms[index]将给出ArrayIndexOutOfBoundsException

所以现在的问题是,为什么当index == denoms.length - 1时它将return 1 index == denoms.length - 1

原因很简单,因为最后一个面额仅为1美分!

amountRemaining任何amountRemaining都只有一种方法可以解决1美分的问题。

因此,如果将数组更改为{25, 10, 5, 2} makeChange(3) {25, 10, 5, 2} ,则会发现解决方案已损坏(当涉及{25, 10, 5, 2} makeChange(3) {25, 10, 5, 2}makeChange(3)将返回1 ,但是您看,它应该是0 )。

如果要在最后一个符号不是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

编辑:

是仅仅因为我们有cent = 1个特殊值而进行特殊优化,还是采用更通用的优化方法?

您可以称其为优化,但正如我的代码所给出的,您可以使用任意cent = x ,只需按amount % denoms[denoms.length - 1] == 0

如果我没记错的话,这是一个很大的硬币兑换问题

我假设您了解问题所在,因为if (index >= denoms.length - 1) return 1; ,则您的问题针对该行if (index >= denoms.length - 1) return 1; ,因此我将省略说明。

有趣的是,我在Eclipse中使用最小面额分别为1、2、3和4美分[IE 25/10/5/1,OR 25/10/5/2,OR 25/10/5/3,或25/10/5/4]。 在每一次运行中,我都得到了完全相同的解决方案(它们代表了最小的子问题)。 那么为什么会这样呢? 如果我有3美分,那么25/10/5/4的解决方案应该为0,这是没有道理的,因为我不能拿单个硬币(假设最小面额大于该数量需要多少分?但是,如果您将1的返回方式视为找不到解决方案的方式,那该怎么办呢?也就是说,如果1的返回代表空硬币集,其中所取硬币没有达到要求的数量(或没有取硬币)?

取问题(n = 6)中的6美分,面额为25/10/5/4,我们得到5 + none [Invalid]和4 + none [Invalid]的解。 如果最小面额为3,则执行相同的操作,则得到5 +无[无效]和3 + 3。 重复最小的2美分,我们得到5 +无[无效]和2 + 2 +2。重复最小的1美分,我们得到5 + 1和1 +1 ... + 1。被设为2,尽管无效案例!

这是因为您提供的解决方案假定无效方式(但取走了硬币,并且当n的值大于最小面额时,空集被视为一种方式)。 这就是为什么即使当n = 10时(2是最小的面额),我们也会得到4种方式(10、5 * 2、2 * 5和5 + 2 * 2 [无效])。 请注意,无效案例是“最佳解决方案”,这意味着这些案例是与每个面额相关的“最佳”无效案例。

我确实相信,Sayakiss提供的解决方案可消除这种无效计数方式的问题(特殊情况是n = 0返回1种方式,即不带数字)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM