[英]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.