该方法的工作原理如下:
第一个语句是当前递归的停止条件(在所有情况下如果没有这些,那么您最终会出现一个无限循环,最终以 StackOverFlow 结束)
-
最后一行是计算发生的地方。每个语句都通过以下方式将问题简化为更小的块:
-
count( S, m - 1, n ) 减少硬币的数量 (m-1),不包括在下一次递归调用中的最后一个硬币
-
count( S, m, n-S[m-1]) 使用数组中的最后一个硬币,并通过该硬币的价值减少需要达到的总和
考虑这个小例子:
S[] = {1,2) // We have a 1 and 2 cent coin
m = S.length // Consider all possibilities ( = 2)
n = 3 // How many ways can we make 3c
// Obviously with: 1x1c + 1x2c
// and: 3x1c
作为树的递归;左分支=count( S, m - 1, n ),右分支=count( S, m, n-S[m-1]):
m=2;n=3
/ \
m=1;n=3 m=2;n=1
/ \ / \
m=0;n=3 m=1;n=2 m=1;n=1 m=2;n=-1
/ \ / \
m=0;n=2 m=1;n=1 m=0;n=1 m=1;n=0
/ \
m=0;n=1 m=1;n=0
这个递归可以被认为是对这棵树的Pre-order遍历。
如果您然后考虑找到解决方案的方法的条件。所以在 n = 0 的叶子节点处。
每一个都是这样产生的:
第一个解决方案
- m=1;n=3 - 排除最后一个硬币 (2c)
- m=1;n=2 - 使用这个硬币 (1c) 并减少 1
- m=1;n=1 - 使用这个硬币 (1c) 并减少 1
- m=1;n=0 - 使用这个硬币 (1c) 并减少 1
- n = 0 - 一个解 (3x1c)
第二个解决方案
- m=2;n=1 - 使用这个硬币(2c)并减少 2
- m=1;n=1 - 排除最后一个硬币 (2c)
- m=1;n=0 - 使用这个硬币 (1c) 并减少 1
- n = 0 - 一个解 (1x2c + 1x2c)
在每个节点处返回一个值 - 0(无解决方案)或 1(解决方案) - 以添加到找到的解决方案的总数中。一旦递归结束,这个最终值就会被返回,它是解决方案的数量。
一些补充说明:
- 这段代码将只考虑数组
S 中的第一个m 硬币,因此要考虑对方法的初始调用需要m == S.length 的所有可能方式
- 假设每个硬币可以多次使用
使用打印语句修改代码以查看递归:
public static void main(String[] args){
int[] coins = new int[]{1,2};
System.out.println("Final Count = " + count(coins, coins.length, 3, ""));
}
public static int calls = 0;
public static int count( int S[], int m, int n , String from){
calls++;
System.out.print("Call#" + calls + ": " + from + "; m = " + m + "; n = " + n);
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
{
System.out.println(" - Solution Found");
return 1;
}
// If n is less than 0 then no solution exists
if (n < 0)
{
System.out.println(" - No Solution Found n < 0");
return 0;
}
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
{
System.out.println(" - No Solution Found (other Case)");
return 0;
}
System.out.println();
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n , from + "E" ) + count( S, m, n-S[m-1], from + "I" );
}