【问题标题】:recursive backtracking makeChange递归回溯 makeChange
【发布时间】:2016-12-03 05:32:25
【问题描述】:

编写一个 makeChange 方法,该方法使用递归回溯来查找使用便士(1 美分)、镍(5 美分)、角钱(10 美分)和 25 美分(25 美分)为给定金额找零的所有方法。

例如,当找 37 美分时,您可以使用:

  • 1 个季度
  • 1 毛钱和 2 便士
  • 3 毛钱和 7 便士
  • 或其他组合。

您的方法应该接受一个参数:要更改的美分数量。

您的方法的输出应该是每种硬币的所有组合的序列,加起来达到该数量,每行一个。

例如,如果客户端代码包含以下调用:

System.out.println(" P  N  D  Q");
System.out.println("------------");
makeChange(28);

生成的整体输出应该如下:

 P  N  D  Q
------------ [3, 0, 0, 1] [3, 1, 2, 0] [3, 3, 1, 0] [3, 5, 0, 0] [8, 0, 2, 0] [8, 2, 1, 0] [8, 4, 0, 0] [13, 1, 1, 0] [13, 3, 0, 0] [18, 0, 1, 0] [18, 2, 0, 0] [23, 1, 0, 0] [28, 0, 0, 0]

解决此问题的一个关键见解是查看硬币的每种面额(便士、镍等)并尝试该硬币的所有可能数字(1 便士、2 便士、...、28 便士)的概念看看可以从该选择开始进行哪些组合。例如,在上面的输出中,首先显示所有以 3 便士开头的组合,然后显示所有以 8 便士开头的组合,以此类推。

由于回溯涉及探索一组选择,因此您应该在代码中以某种方式表示硬币面额。我们建议保留一份所有硬币面额值的列表以供处理。当您处理各种硬币值时,您可以修改此列表的内容。下面的模板是一个起点(您可以将其复制/粘贴到您的代码文本框中开始):

public static void makeChange(int amount) {
    List coinValues = new LinkedList();
    coinValues.add(1);    // penny
    coinValues.add(5);    // nickel
    coinValues.add(10);   // dime
    coinValues.add(25);   // quarter

// ... your code goes here ...

您可以假设传递给您的方法的变化量是非负的,但它可能超过 100。

这是我的代码:

public static void makeChange(int amount){
    int[] acc = new int[4];
    makeChange(amount, acc);
}

private static void makeChange(int amount, int[] acc){
    if(amount == 0){
        System.out.print("[" + acc[0]);
        for(int i = 1; i < 4; i++){
            System.out.print(", " + acc[i]);
        }
        System.out.print("]");
        System.out.println();
    }
    if(amount > 0){
        acc[0]++;
        makeChange(amount - 1, acc);
        acc[0]--;
        acc[1]++;
        makeChange(amount - 5, acc);
        acc[1]--;
        acc[2]++;
        makeChange(amount - 10, acc);
        acc[2]--;
        acc[3]++;
        makeChange(amount - 25, acc);
        acc[3]--;
    }
}

及其调用 makeChange(28) 的输出:

[28, 0, 0, 0]
[23, 1, 0, 0]
[23, 1, 0, 0]
[23, 1, 0, 0]
[23, 1, 0, 0]
[23, 1, 0, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 0, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 0, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 0, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 0, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 0, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 0, 1, 0]
[13, 1, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 0, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 0, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 0, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[13, 1, 1, 0]
[23, 1, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]
[18, 2, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 3, 0, 0]
[13, 1, 1, 0]

...(有数百行输出)

谁能告诉我为什么会产生重复的输出? 非常感谢!

【问题讨论】:

  • 您是否在调试器中单步执行您的代码?你发现了什么?
  • 看到我的电脑出现了这个奇怪的问题,目前无法调试......所以我只能在脑海中运行代码。

标签: java backtracking recursive-backtracking


【解决方案1】:

简单地说,您通过不同的途径获得了相同的解决方案。现在编写代码的方式顺序很重要。

简单易懂的案例:

假设我们想要更改 6。现在是 1 + 5 = 5 + 1

因此,您将在解决方案中有两次 [1,1,0,0]。

方式一:你先减1,再减5,到0。

方式2:你先减5,再减1,达到0。

要获得唯一的解决方案,请使用HashSetTreeSet 将解决方案存储为ArrayList(但不是数组),而不是在基本条件下打印。最后将所有解决方案一起打印出来。

另一种方法是您可以添加第三个参数,指示您最后使用的硬币。您将使用面额 &gt;= 的硬币到最后使用的硬币。这可确保解决方案是独一无二的。

【讨论】: