【问题标题】:minimum number of coins to make change找零的最少硬币数量
【发布时间】:2018-11-05 13:23:47
【问题描述】:

我正在尝试打印最少数量的硬币来进行更改,如果不可能,请打印 -1

在此代码中,变量 int[] c(硬币数组)具有我可以用来计算总和的面额。

int total 有我需要使用硬币计算的总和(无限供应)

    public static int mincoinDP(int[] c, int total) {
        int[][] a = new int[c.length + 1][total + 1];

        for (int i = 0; i <= c.length; i++) {
            a[i][0] = 0;
        }
        for (int j = 1; j <= total; j++) {
            a[0][j] = Integer.MAX_VALUE - total;
        }

        for (int i = 1; i <= c.length; i++) {
            for (int j = 1; j <= total; j++) {
                if (c[i - 1] > j) {
                    a[i][j] = Integer.MAX_VALUE - total;
                } else {
                    a[i][j] = Math.min(a[i - 1][j], 1 + a[i][j - c[i - 1]]);
                }
            }
        }

        return a[c.length][total];
    }

对于 Sum : 4759 和 Array: {31 90 8 36} 正确的输出是:59 我的输出是:60

代码有什么问题?

以下是我的递归解决方案,尝试在 DP 解决方案中应用相同的逻辑。这里的逻辑似乎也有问题。对于相同的输入,它会打印 -2147483595

    public static void main(String[] args) {
        int[] array = new int[] {31, 90, 8, 36};
        System.out.println(mincoin(array, 4759, 0));
    }

    public static int mincoin(int[] c, int total, int i) {

        if (total == 0) return 0;
        if (i >= c.length) return Integer.MAX_VALUE;


        int x = Integer.MAX_VALUE, y = Integer.MAX_VALUE;

        if (total - c[i] >= 0) {
            x = 1 + mincoin(c, total - c[i], i);
        }
        y = mincoin(c, total, i + 1);

        return Math.min(x, y);
    }

编辑:代码中的问题是:

  1. DP 版本: if (c[i -1] > j) , 解决方案不成立的情况 可能选择这枚硬币:在这里我们应该接受没有的解决方案 这枚硬币是 a[i-1][j]
  2. 递归版本:if(i >= c.length), 当我们在这个位置没有任何硬币时,它是终止条件, 在这里我们应该返回无穷大(Integer.MAX_VALUE)和 避免整数溢出返回 Integer.MAX_VALUE - 总计。

虽然我不喜欢这个版本的infinity,但除了这里之外,我没有看到任何好的方式。

【问题讨论】:

  • 能否请您解释一下您对此的意图是什么,在您的意见中循环中应该发生什么。请同时解释你的变量,或者至少让它们更有意义。
  • 不要只打印硬币的总数,而是打印每个硬币的计数以找出问题。
  • 另外,您的递归代码是否有效,还是显示相同的问题?
  • 根据 cmets 的要求添加了更多相关细节。

标签: java algorithm dynamic-programming


【解决方案1】:

以防万一有人在寻找解决方案

public int coinChange(int[] coins, int amount) {
    int dp[][] = new int[coins.length+1][amount+1];
    Arrays.sort(coins);
    
    // First column of every row
    for (int i = 0; i < coins.length; ++i) {
        dp[i][0] = 0;
    }

    /*
       Setting this so that this is default max value. We always
       want our dp[i][j] better than this
    */
    for (int j = 0; j <= amount; ++j) {
        dp[0][j] = amount+1;
    }
    
    for (int i = 1; i <= coins.length; ++i) {
        for (int j = 1; j <= amount; ++j) {
            if (coins[i-1] > j) {
               dp[i][j] = dp[i-1][j];   // Take the already best value in above row
            } else {
               dp[i][j] = Math.min(dp[i-1][j], 1 + dp[i][j-coins[i-1]]); // Take the best of above row and using current coin
            }
        }
    }
    if (dp[coins.length][amount] > amount) { // it means we cannot find any coin
        return -1;
    } else {
        return dp[coins.length][amount];
    }
}

【讨论】:

    【解决方案2】:

    您似乎正在使用动态编程,a[i][j] 旨在表示总和为j 的硬币的最小数量(使用第一个 i 面额)。但我认为你的重复关系已经关闭。它们应该是:

    a[0][j] = 0 if j==0, otherwise infinity
    
    a[i][j] = a[i-1][j] if c[i-1] > j
    a[i][j] = min(a[i-1][j], 1 + a[i][j-c[i-1]]) if c[i-1] <= j
    

    主要错误是代码中的 if c[i-1] &gt; j 大小写。您将值设置为无穷大(或无穷大的变体),但您应该只复制前一行中最小数量的硬币,因为您可以使用较少数量的硬币来构建总数。

    顺便说一句,有一种更简洁的方式来编写这段代码。在伪代码中:

    a = new int[total+1]
    for int j = 1 to total+1 {
        a[j] = infinity
    }
    for int coin in coins {
        for j = coin to total+1 {
            a[j] = min(a[j], a[j-coin]+1)
        }
    }
    

    它本质上是相同的算法,但它使用了一个更小的一维数组,它可以就地修改。

    【讨论】:

    • 你所说的主要错误正是我正在寻找的答案,附加评论也很有帮助。
    • 不客气。我认为您可以通过测试小案例自己进行更多调试。例如,coins [1, 2] total 1 将失败。一旦你有了一个很小的复制案例,调试起来就会容易得多,因为你可以手动运行代码来看看出了什么问题。
    • 不幸的是,我尝试过的小测试用例都成功了,在查看问题后想到它会在 [1, 2] 和总计上失败。
    猜你喜欢
    • 2020-05-09
    • 2019-07-17
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多