【问题标题】:How to solve Coin Change problem with amount being a double instead of an int如何解决金额为 double 而不是 int 的硬币找零问题
【发布时间】:2020-07-05 20:18:34
【问题描述】:

我将如何解决类似的问题,其中amount 可以是double 值(例如19.12)而不是严格意义上的int(例如19),并且测试用例将包括美元钞票和硬币?

在典型的 leetcode 问题中,我将创建一个 dp 数组,该数组将存储最少的硬币 # 以构成当前 amount 的任何内容。使用自下而上的处理,我将从最小的子问题 (dp[0]=0) 开始处理手头的问题,如下所示:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        for(int i = 0; i <= amount; i++) {
            for(int j = 0; j < coins.length; j++) {
                if(coins[j] <= i) {
                    dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]]);
                }
            }
        }
        return dp[amount] > amount ? - 1 : dp[amount];
    }
}

编辑

Joni's answer 成功了。这是我的最终解决方案。基本上,原始解决方案只能读取int 金额(0 美分的整数金额),但现在,下面的程序将double 金额分成纸币和硬币,并将两者转换为int 值。然后,无论原来的解决方案在做什么,新的解决方案只是分别对纸币和硬币做同样的事情。不确定这是否有意义,但这是解决方案:

import java.util.Arrays;
import java.util.Scanner;

class Main {
    public static void main (String[] args) {
        int[] billDeno = new int[] {500,200,100,50,20,10,5,2,1};
        int[] coinDeno = new int[] {50,25,10,5,2,1};
        Scanner input = new Scanner(System.in);
        System.out.printf("Enter amount: ");
        double amount = input.nextDouble();
        int y = coinChange(billDeno, coinDeno, amount);
        System.out.println(y);
    }
    
    public static int coinChange(int[] billDeno, int[] coinDeno, double amount) {        
        int numDollars = (int) amount;
        int numCents = (int) ((amount - numDollars) * 100 + 0.5f);
        
        int[] dpDollars = new int[numDollars + 1];
        int[] dpCents = new int[numCents + 1];
        
        Arrays.fill(dpDollars, numDollars + 1);
        Arrays.fill(dpCents, numCents + 1);
        
        dpDollars[0] = 0;
        dpCents[0] = 0;
        
        for(int i = 0; i <= numDollars; i++) {
            for(int j = 0; j < billDeno.length; j++) {
                if(billDeno[j] <= i) {
                    dpDollars[i] = Math.min(dpDollars[i], 1 + dpDollars[i - billDeno[j]]);
                }
            }
        }
        for(int i = 0; i <= numCents; i++) {
            for(int k = 0; k < coinDeno.length; k++) {
                if(coinDeno[k] <= i) {
                    dpCents[i] = Math.min(dpCents[i], 1 + dpCents[i - coinDeno[k]]);
                }
            }
        }
        return dpDollars[numDollars] + dpCents[numCents] > amount ? -1 : dpDollars[numDollars] + dpCents[numCents];
    }
}

这可能看起来像是一个 hacky 解决方案(尤其是接近尾声),所以请随时发布更优化的解决方案。感谢大家的帮助!

【问题讨论】:

  • 双精度是指小数点后两位数的双精度数吗?只需将数字乘以 100 并使用整数。

标签: java dynamic-programming coin-change


【解决方案1】:

这不适用于double。

许多十进制小数(例如 0.10)不能完全表示为二进制浮点数。当您在 Java 程序中编写 0.10 或 0.30 时,您会得到最好的近似值,但不是精确值。见Is floating point math broken?

例如,0.30 不是0.10 的三倍。该程序将找不到对 0.3 进行更改的方法。

如果您将金额和硬币价值乘以 100,那么它们的单位是美分,并使用 int 或 long 来衡量,这样会起作用。也可以使用 BigDecimal 类而不是 double,但是您必须使用方法调用而不是算术运算符。

【讨论】:

  • 谢谢@Joni。我已经更新了问题和代码。我解释了我现在遇到的障碍。
【解决方案2】:

现在有一个很好的答案here。

我想如果您将int 更改为double 可能会起作用,也可能不会。但是它不会通过 LeetCode,因为这不是所需的输入/输出:

class Solution {
    public double coinChange(double[] coins, double target) {
        if (target < 1)
            return 0;
        return helper(coins, target, new double[target]);
    }

    private double helper(double[] coins, double rem, int[] count) {
        if (rem < 0)
            return -1;
        if (rem == 0)
            return 0;
        if (count[rem - 1] != 0)
            return count[rem - 1];
        int min = Integer.MAX_VALUE;
        for (double coin : coins) {
            double res = helper(coins, rem - coin, count);
            if (res >= 0 && res < min)
                min = 1 + res;
        }

        count[rem - 1] = (min == Integer.MAX_VALUE) ? -1 : min;
        return count[rem - 1];

    }
}

您的输入/输出应该如下所示:

coins = [1.0, 2.0, 5.0, 0.01, 0.1, 0.25, 0.5], amount = 19.12

您可能必须调试它,可能有一些 type 错误。


对于int,这将通过 LeetCode:

class Solution {
    public int coinChange(int[] coins, int target) {
        if (target < 1)
            return 0;
        return helper(coins, target, new int[target]);
    }

    private int helper(int[] coins, int rem, int[] count) {
        if (rem < 0)
            return -1;
        if (rem == 0)
            return 0;
        if (count[rem - 1] != 0)
            return count[rem - 1];
        int min = Integer.MAX_VALUE;
        for (int coin : coins) {
            int res = helper(coins, rem - coin, count);
            if (res >= 0 && res < min)
                min = 1 + res;
        }

        count[rem - 1] = (min == Integer.MAX_VALUE) ? -1 : min;
        return count[rem - 1];

    }
}

参考

  • 有关其他详细信息,您可以查看Discussion Board。有很多公认的解决方案,其中包含各种languages 和解释、高效算法以及渐近的time/space 复杂性分析1, 2。

【讨论】:

    猜你喜欢
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    • 2020-05-09
    • 2019-07-17
    相关资源
    最近更新 更多