【问题标题】:Printing which coins are used to make a given amount打印用于制作给定金额的硬币
【发布时间】:2015-10-18 19:38:01
【问题描述】:

我正在尝试使用递归来找到最小数量的硬币来达到给定的数量。我的代码能够列出所需的最少硬币数量,但我似乎找不到打印出用于提出解决方案的硬币的方法。我已经搜索并找到了类似的示例,但我似乎无法正确地将其应用于此。

这是我目前所拥有的:

import java.util.*;

public class Coins{

    public static int findMinCoins(int[] currency, int amount) {
        int i, j, min, tempSolution;

        min = amount;

        for (i = 0; i < currency.length; i++) {
            if (currency[i] == amount) {
                return 1;
            }
        }

        for (j = 1; j <= (amount / 2); j++) {
            tempSolution = findMinCoins(currency, j) + findMinCoins(currency, amount - j);
            if (tempSolution < min) {
                min = tempSolution;
            }
        }
        return min;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int[] USA =
        {1, 5, 10, 25, 50};
        System.out.println("Please enter an integer amount.");
        int amount = in.nextInt();
        int minCoins = findMinCoins(USA, amount);
        System.out.println("The minimum number of coins to make " + amount + " in United States currency is " + minCoins + ".");
        System.out.println("The coins used were:");
        /*Print coins used to find minCoins.*/
        in.close();
    }
}

目前运行的代码示例:

Please enter an integer amount.
17
The minimum number of coins to make 17 in United States currency is 4.
The coins used were:

如果有人能给我一些关于如何做到这一点的见解,将不胜感激。

【问题讨论】:

  • 什么是可用的硬币?另外,尝试描述它应该如何工作 - 当前代码对于某些输入(例如 - 44)永远运行
  • 关于质量的旁注:请考虑源代码中的命名。这很糟糕。首先,有约定;例如类名以大写开头。那么:一个名字应该说明它所命名的事物是关于什么的。所以,数字是一个数字,而价值是一个价值……嗯?对于实际问题,您可能希望在这里和那里开始使用 printlns ......只是为了打印中间结果;这可能会给你一个线索......在哪里以及如何添加相关的打印输出。
  • @chenchuk 可用的硬币可以是任何一组整数。对于我发布的代码,我只使用了 {1,5,10,25,50}。至于结果,它打印出字符串“以美国货币制作 17 的最小硬币数量为 4”。如果输入 17。我不确定如何在不使用动态编程的情况下以更高的值改进运行时。
  • @Jägermeister 好的,我在编辑中修复了一些质量问题,如果您发现任何其他问题,请随时纠正我,因为我正在努力使我的代码更清晰。至于添加 printlns,我尝试在第一个 for 循环中创建一个列表来存储 currency[i] 的每个值,但我在那里没有取得任何进展。除此之外,我已经花时间逐行​​调试,但我仍然不知道从哪里开始。
  • @Sigonious 您不需要使用递归,请参阅下面的答案。它有测试证明它有效。

标签: java algorithm recursion coin-change


【解决方案1】:

我认为这应该完全符合您想要实现的目标。只需调用public static int findMinCoins(arg1, arg2),它会输出使用递归算法使用的最小硬币数量和所有特定硬币(它会显示其出现次数)。

public static int findMinCoins(int[] currency, int amount) {
    int min = findMinCoins(currency, amount, 0);
    System.out.print("The coins used were: ");
    return min;
}
private static int findMinCoins(int[] currency, int amount, int min){
    int number, value1, value2;
    int min1 = min;
    for(int i=currency.length-1; i>=0; i--) {
        if (amount>=currency[i]){
            amount = amount - currency[i];
            System.out.print(currency[i] + " ");
            min1 = findMinCoins(currency, amount, min1);
            return ++min1;
        }
    }
    return min1;
}

【讨论】:

  • 如果货币数组的大小发生变化,这将不起作用。
  • @GáborLipták 现在,请让您的手腕再疼一点,以消除反对票,并为我的回答提供一些积极的反馈!
  • @Mr.Robot 我没有投反对票。我不是故意冒犯的,对不起。我只是想以一种有趣的方式表达,这种重复的代码一定会引起我们开发人员的良心不安。如果我冒犯了,再次抱歉。
  • @Mr.Robot 作为正面反馈:上面的代码比以前好很多。也许返回一个硬币列表会是一个更好的解决方案,因为 list.size() 将提供硬币的数量,并且列表中的项目可以“解释”解决方案。这种特殊情况可能选择递归解决方案并不是最好的,因为输入更多的钱很容易导致堆栈溢出。可能线性解决方案更好。您为提供解决方案所做的努力仍然值得赞赏。
  • @GáborLipták 感谢您的反馈。是的,递归部分可能会导致堆栈溢出,但线性解决方案仍然更好。提供一个硬币列表,你不认为当它匹配时打印出硬币价值,就像我在答案中那样满足要求吗?
【解决方案2】:

这是一个递归代码(工作,但需要修复......)。 这个想法是传递所有硬币 {1,5,10,25,50} 并进行数组,并从左到右递归调用(直到数组结束)

注意事项:

输出有个小bug

数字作为 1 个元素的数组而不是原始 int 传递。 (这是为了通过所有递归调用来保持其值的引用类型:

public class coins {

    public static void main(String[] args) {
        // arrays of coin types
        int[] coinTypes = { 0, 1, 5, 10, 25, 50 };
        // arrays are references, so changing them
        // inside the recursive function will 'really' change
        int[] price = {11}; // sample input
        tryBigger(price, coinTypes, 0);
    }

    // tries to see if higher coin is possible by passing array
    // of coin types and recursively call until the end of array
    public static void tryBigger(int[] price, int[] theCoins, int index) {
        if (index < theCoins.length-1){
            // until last element
            if (price[0] > theCoins[index]){
                tryBigger(price, theCoins, ++index);
            }
        }
        // find amount of this coin
        int thisCoin = price[0]/theCoins[index];
        // remove the amount already got before
        price[0] = price[0] - thisCoin*theCoins[index];
        System.out.println(thisCoin + " coins of " + theCoins[index]);
        return;

    }
}

【讨论】:

  • 这种输出格式是我要找的,但我相信这使用了“作弊”的方法来解决它。例如,如果您使用货币集 {1,2,3,5,7,11,13} 并将价格更改为 22,您将得到 13、7 和 2 硬币的结果,而不是两个 11硬币。还是谢谢你的回答。
  • 这在算法上有很大的不同。我的算法试图拥有尽可能多的硬币,而你的硬币数量最少(任何类型) - 这就是你的意思吗?
【解决方案3】:

在您提供的代码中,递归函数返回数字 1 或 min。使用此方法,获取硬币列表的一种方法是更改​​返回变量以同时包含硬币和数量。

这是一个总体思路的示例;由于我不了解Java编程,所以我将实现留给你。

if (currency[i] == amount){
    return [1,i];

...

temp1 = findMinCoins(currency,j);
temp2 = findMinCoins(currency,amount - j);

if(temp1[0] + temp2[0] < min[0]){
    min = [temp1[0] + temp2[0]] concatenated with temp1[1..] and temp2[1..]

【讨论】:

    【解决方案4】:

    给你,你有一个测试,功能如下。请注意,不处理边缘情况,例如空货币或负金额。

    假设货币数组已排序。如果没有,请使用Arrays.sort(currency) 对其进行排序。

    public class FindMinimumCoinsTest {
    
      @Test
      public void test() throws Exception {
          int[] USA = { 1, 5, 10, 25, 50 };
          assertEquals(2, findMinCoins(USA, 11));
          assertEquals(4, findMinCoins(USA, 8));
          assertEquals(4, findMinCoins(USA, 111));
          assertEquals(3, findMinCoins(USA, 27));
      }
    
      public static int findMinCoins(int[] currency, int amount) {
          int coins = 0;
          int sum = 0;
          int value, n;
    
          for (int i = currency.length - 1; i >= 0; i--) {
              value = currency[i];
              n = (amount - sum) / value;
              if (n > 0) {
                  coins += n;
                  sum += n * value;
              }
          }
          return coins;
        }
    }
    

    另外,使用这种方法不需要递归;)

    【讨论】:

    • 感谢您的回答,但是这个程序的一个要求是它使用递归来找到解决方案。我能够找到一个动态编程解决方案,但似乎无法找到一种方法来打印最小硬币以及使用哪些硬币使用递归来找到最小值。
    【解决方案5】:

    我正在研究类似的东西,这就是我想出的。您可以将使用过的硬币保存在单独的数组中,并使用辅助函数递归地打印最后使用过的硬币。如果你想返回一个列表或一个字符串,你可以让助手创建并返回一个。

    /**
    * FIND MINIMAL NUMBER OF COINS TO MAKE CHANGE, WITH CHANGE VALUES: 1, 2, 5, 10, 20, 50, 100, 200
    *
    * @param change The amount of change we need to give
    * @return Minimal amount of coins used
    */
    public static int minimalNumberOfCoinsToMakeChange(int change) {
        int[] denominations = {1, 2, 5, 10, 20, 50, 100, 200};
        int[] dp = new int[change + 1];
        int[] origins = new int[change+1];
        dp[0] = 0;
        for (int i = 1; i <= change; i++) {
            dp[i] = Integer.MAX_VALUE;
            for (int coin : denominations) {
                if (i - coin < 0) {
                    continue;
                }
                dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
                origins[i] = coin;
            }
        }
        if (dp[change] == Integer.MAX_VALUE) {
            return -1;
        }
        printPath(origins);
        return dp[change];
    }
    
    /**
     * HELPER FUNCTION - GET PATH
     *
     * @param origins Array with origins from main function
     */
    private static void printPath(int[] origins) {
        int last = origins[origins.length-1];
        System.out.println(last);
        origins = Arrays.copyOfRange(origins,0,origins.length-last);
        if (origins.length-1 > 0){
            printPath(origins);
        }
    }
    

    我在本例中对面额数组进行了硬编码,但您应该能够将其删除并传递另一个作为参数。这不是最有效的方法,但可能对像我这样刚刚进入动态编程的人有所帮助。干杯!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 2011-07-22
      • 2015-12-22
      • 2019-06-27
      相关资源
      最近更新 更多