简体   繁体   English

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

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

Working on the algorithm puzzle of calculating the number of ways of representing n cents, using unlimited number of 25 cents, 10 cents, 5 cents and 1 cents. 使用不限数量的25美分,10美分,5美分和1美分来研究计算n美分的方式数量的算法难题。 Referred a few different solutions and have a question about below solution, I am not sure why we are confident when if (index >= denoms.length - 1) there must be a solution, and there must be a unique solution? 提到了几种不同的解决方案,并对下面的解决方案有疑问,我不确定为什么我们有把握if (index >= denoms.length - 1)必须有解决方案,并且必须有唯一的解决方案? Is it a special optimization just because we have cent = 1 special value, or a more general optimization approach? 是仅仅因为我们有cent = 1个特殊值而进行特殊优化,还是采用更通用的优化方法? Appreciate for more insights. 欣赏更多见解。

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));
        }
    }

}

If it doesn't return when index > denoms.length - 1 , then denoms[index] will give ArrayIndexOutOfBoundsException . 如果在index > denoms.length - 1时不返回,则denoms[index]将给出ArrayIndexOutOfBoundsException

So question now is why it will return 1 when index == denoms.length - 1 ? 所以现在的问题是,为什么当index == denoms.length - 1时它将return 1 index == denoms.length - 1

The reason is quite simple, becasue the last denom is just 1 cent! 原因很简单,因为最后一个面额仅为1美分!

Any amountRemaining will have just one way to do with 1 cent. amountRemaining任何amountRemaining都只有一种方法可以解决1美分的问题。

So, if you change your array to {25, 10, 5, 2} , you will find your solution broken down(when it comes to {25, 10, 5, 2} , makeChange(3) will return 1 , but you see, it should be 0 ). 因此,如果将数组更改为{25, 10, 5, 2} makeChange(3) {25, 10, 5, 2} ,则会发现解决方案已损坏(当涉及{25, 10, 5, 2} makeChange(3) {25, 10, 5, 2}makeChange(3)将返回1 ,但是您看,它应该是0 )。

If you want to fix your solution when the last denom is not 1 cent, you should just change if (index >= denoms.length - 1) return 1 to: 如果要在最后一个符号不是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;
    }
}

Then with {25, 10, 5, 2} : 然后用{25, 10, 5, 2}

makeChange(1) = 0
makeChange(2) = 1
makeChange(3) = 0

EDIT: 编辑:

Is it a special optimization just because we have cent = 1 special value, or a more general optimization approach? 是仅仅因为我们有cent = 1个特殊值而进行特殊优化,还是采用更通用的优化方法?

You can call it an optimisation, but as my code gives, you can do it with any cent = x , just by amount % denoms[denoms.length - 1] == 0 . 您可以称其为优化,但正如我的代码所给出的,您可以使用任意cent = x ,只需按amount % denoms[denoms.length - 1] == 0

If I'm not mistaken, this is very much a coin changing problem . 如果我没记错的话,这是一个很大的硬币兑换问题

I assume that you understand the problem, since that your question is very specific towards that line if (index >= denoms.length - 1) return 1; 我假设您了解问题所在,因为if (index >= denoms.length - 1) return 1; ,则您的问题针对该行if (index >= denoms.length - 1) return 1; , so I will skip the explanation. ,因此我将省略说明。

Interestingly enough, I ran your whole code in Eclipse using smallest denominations of 1, 2, 3 and 4 cents [IE 25/10/5/1, OR 25/10/5/2, OR 25/10/5/3, OR 25/10/5/4]. 有趣的是,我在Eclipse中使用最小面额分别为1、2、3和4美分[IE 25/10/5/1,OR 25/10/5/2,OR 25/10/5/3,或25/10/5/4]。 In every one of the runs, I got the exact same solution (these represent the smallest sub-problems). 在每一次运行中,我都得到了完全相同的解决方案(它们代表了最小的子问题)。 So why is this the case? 那么为什么会这样呢? Wouldn't it make sense that if I had (for example) 3 cents, the solution of 25/10/5/4 should be 0 since I can't take a single coin (given that the smallest denomination is greater than the amount of cents required? However, what if you think of the 1 way it returns as the way that there is no solution found? That is, if the 1 return represents the empty set of coins where coins taken do not hit the required amount (or no coins are taken)? 如果我有3美分,那么25/10/5/4的解决方案应该为0,这是没有道理的,因为我不能拿单个硬币(假设最小面额大于该数量需要多少分?但是,如果您将1的返回方式视为找不到解决方案的方式,那该怎么办呢?也就是说,如果1的返回代表空硬币集,其中所取硬币没有达到要求的数量(或没有取硬币)?

Taking 6 cents in the question (n = 6), with denominations 25/10/5/4, we obtain the solutions of 5 + nothing[Invalid], and 4 + nothing[Invalid]. 取问题(n = 6)中的6美分,面额为25/10/5/4,我们得到5 + none [Invalid]和4 + none [Invalid]的解。 Doing the same if the smallest denomination is 3, we get 5 + nothing[Invalid], and 3+3. 如果最小面额为3,则执行相同的操作,则得到5 +无[无效]和3 + 3。 Repeating for 2 cents smallest, we get 5 + nothing[Invalid], and 2 + 2 + 2. Repeating for 1 cent smallest, we get 5 + 1 and 1 + 1 ... + 1. The total for every solution ends up to be 2, despite the invalid cases! 重复最小的2美分,我们得到5 +无[无效]和2 + 2 +2。重复最小的1美分,我们得到5 + 1和1 +1 ... + 1。被设为2,尽管无效案例!

This is because the solution which you provided assumes the invalid ways (but with coins taken, and an empty set when the value of n is larger than the smallest denomination) are counted as one way each. 这是因为您提供的解决方案假定无效方式(但取走了硬币,并且当n的值大于最小面额时,空集被视为一种方式)。 That's why even for when n = 10 (with 2 being the smallest denomination), we get 4 ways (10, 5*2, 2*5 and 5 + 2*2 [Invalid]). 这就是为什么即使当n = 10时(2是最小的面额),我们也会得到4种方式(10、5 * 2、2 * 5和5 + 2 * 2 [无效])。 Do note that the invalid cases are "optimal solutions", meaning that these are the "best" invalid cases which are linked to each denomination. 请注意,无效案例是“最佳解决方案”,这意味着这些案例是与每个面额相关的“最佳”无效案例。

I do believe that the solution provided by Sayakiss works towards eliminating this problem of invalid ways being counted (with the special case of n = 0 returning 1 way, which is to take no numbers). 我确实相信,Sayakiss提供的解决方案可消除这种无效计数方式的问题(特殊情况是n = 0返回1种方式,即不带数字)。

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

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