用一定数量的硬币返回更改

时间:2015-05-16 21:26:14

标签: java

我想实现一台自动咖啡自动售货机。机器预先装满了硬币(例如10个5美分硬币,20个10美分硬币等)。

我希望用户输入一定数量(插入货币)减去咖啡的价格(例如120美分),然后根据自动售货机中可用的硬币给予更改。

我是这样开始的:

final int [] coins = {200, 100, 50, 20, 10, 5}; 

...但我怎么能给他们一个像10 = 5的值,因为有5个10美分的硬币?

2 个答案:

答案 0 :(得分:3)

您可以将它们存储在地图中 - 例如HashMap

然后您将使用硬币的值作为键,并使用金额作为值。 例如:

Map<Integer, Integer> coins = new HashMap<>();
coins.put(10, 5); //5 10-cent coins

要获得10美分硬币的金额,你可以这样做:

coins.get(10);

答案 1 :(得分:2)

public class CoinDispenser {
    private final Map<Coin, Integer> coinCount = getDefaultMap();

    public void addCoins(Coin type, int amount) {
        coinCount.put(type, amount + getCoinCount(type));
    }

    public int getCoinCount(Coin type) {
        return coinCount.get(type);
    }

    // Why not use an empty map you ask? Map.get will return null
    // if the key does not exist. So for the default map we get all
    // the possible coins and add them to the map with the default
    // count of zero
    private static Map<Coin, Integer> getDefaultMap() {
        Map<Coin, Integer> ret = new HashMap<>();
        for (Coin c: Coin.values())
            ret.put(c, 0);
        return ret;
    }

    /**
     * Get the change in the available coins. Please note that
     * this removes the coins from the vending machine.
     */
    public Map<Coin, Integer> getChange(int amount) {
        Map<Coin, Integer> ret = getDefaultMap();

        if (getChangeImpl(amount, true, getDefaultMap())) {// if we have the right coins...
            getChangeImpl(amount, false, ret);// do the real thing
            return ret;
        }

        throw new IllegalArgumentException("Insufficient coins!!");// fall through case
    }

    /** The private version of get change...
     *
     * @param amount The amount of change needed
     * @param isMock If true, this will not remove coins from the dispenser
     * @param out The output map
     * @return True, if we have the proper coins
     */
    private boolean getChangeImpl(int amount, boolean isMock, Map<Coin, Integer> out) {
        List<Coin> types = Arrays.asList(Coin.values());// get the coin types, in least to greatest order...
        Collections.reverse(types);// flip the array because we need the big coins to go first
        int amountLeft = amount;// how much change do we still need to make up?

        for (Coin c: types)
            while (amountLeft >= c.value && getCoinCount(c) > 0) {// while the coin is the right
                                                                    // value and we still have it
                amountLeft -= c.value;// de-inc amountLeft by the coins value
                out.put(c, out.get(c) + 1);// add the coin to the out
                if (isMock == false)// if this is the real thing
                    addCoins(c, -1);// remove one coin from inventory
            }


        return amountLeft == 0;// if we don't have the correct coins, this won't be zero
    }

    // This belongs in it's own file
    public enum Coin {
        Penny       (1),// each coin and it's value
        Nickle      (5),
        Dime        (10),
        Quarter     (25),
        HalfDollar  (50);

        /**
         * The value of the coin in cents.
         */
        public final int value;

        Coin(int value) {this.value = value;}
    }

    public static void main(String[] args) {
        CoinDispenser vm = new CoinDispenser();
        vm.addCoins(Coin.Penny, 45);
        vm.addCoins(Coin.Dime, 3);
        vm.addCoins(Coin.Quarter, 1);

        System.out.println(vm.getChange(36));// 1 quarter, 1 dime, 1 penny
        System.out.println(vm.getChange(36));// 2 dimes, 6 pennies
        System.out.println(vm.getChange(36));// 36 pennies
        System.out.println(vm.getChange(36));// IllegalArgumentException: Not enough coins!!
    }
}

我是怎么来到这里的

第1步:有什么要求?你应该能够

  • 添加硬币和
  • 删除硬币

来自硬币分配器。

第2步:开始编码!!

首先,您需要弄清楚如何代表硬币。 IMO最好的方法是使用Coin enum<Coin, Integer>类型的地图来存储硬币。 Coin枚举的示例:

public enum Coin {
    Penny       (1),// each coin and it's value
    Nickle      (5),
    Dime        (10),
    Quarter     (25),
    HalfDollar  (50);

    /** The value of the coin in cents. */
    public final int value;

    Coin(int value) {this.value = value;}
}

Map<Coin, Integer> coinCount = new HashMap<>();

(如果您不熟悉enum,则每个条目基本上都被命名为该类的最终静态实例)

所以,现在我们有了存储硬币的方法,我们将创建一个包装和操作这些数据的类。 CoinDispenser听起来像个好名字。

public class CoinDispenser {
    private final Map<Coin, Integer> coinCount = new HashMap<>();

    public enum Coin {...}
}

现在我们可以开始添加功能了。从简单开始,addCoinsgetCoinCount功能听起来不错。

public class CoinDispenser {
    private final Map<Coin, Integer> coinCount = getDefaultMap();

    public void addCoins(Coin type, int amount) {
        coinCount.put(type, amount + getCoinCount(type));
    }

    public int getCoinCount(Coin type) {
        return coinCount.get(type);
    }

    // We can't start with an empty map
    private static Map<Coin, Integer> getDefaultMap() {
        Map<Coin, Integer> ret = new HashMap<>();
        for (Coin c: Coin.values())
            ret.put(c, 0);
        return ret;
    }

    public enum Coin {...}
}

非常简单,对吧?是时候继续下一个方法getChange(int amount)了。这种方法的算法非常简单,摆脱了大硬币的拳头。这是v1:

public Map<Coin, Integer> getChange(int amount) {
    List<Coin> types = Arrays.asList(Coin.values());
    Collections.reverse(types);

    Map<Coin, Integer> ret = new HashMap<>();
    fillWithDefault(ret);
    int amountLeft = amount;

    for (Coin c: types) {
        while (amountLeft >= c.value && getCoinCount(c) > 0) {
            amountLeft -= c.value;
            ret.put(c, ret.get(c) + 1);
            addCoins(c, -1);
        }
    }

    return ret;
}

基本上我们获得所有硬币类型,至少按最大顺序对它们进行排序,并创建一个地图以填充返回的更改。接下来,我们遍历所有硬币并检查每个硬币以查看它是否有效(它的值小于剩余的金额,我们在库存中有硬币),如果它能够工作,我们将剩下的金额减去通过硬币值,将硬币添加到退货中,并将其从创新中移除。但是有一个错误。当你试图透支时会发生什么?

地图中会返回零币!为什么?我们从不检查amountLeft中的getChange var。退货时剩余的金额不是0,因为我们的库存中没有足够的硬币。解决方法是将此方法拆分为2种方法,一种检查是否有足够的硬币,另一种检查硬币。这就是它的样子:

public Map<Coin, Integer> getChange(int amount) {
    Map<Coin, Integer> ret = getDefaultMap();
    if (getChangeImpl(amount, false, getDefaultMap())) {// if we have the right coins...
        getChangeImpl(amount, true, ret);// Do the real thing
        return ret;
    }

    throw new IllegalArgumentException("Insufficient coins!!");// fall through case
} 

private boolean getChangeImpl(int amount, boolean isMock, Map<Coin, Integer> out) {    
    ...
    for (Coin c: types)
        while (amountLeft >= c.value && getCoinCount(c) > 0) {
            ...
            if (isMock == false)// if this is the real thing
                addCoins(c, -1);// remove one coin from inventory
        }
    return amountLeft == 0;// if we have the correct coins, this will be zero
}

我们有最后一个错误。我们假设我们有1 Quarter5 Dimes,然后我们尝试绘制50美分,它会抛出IllegalArgumentException("Insufficient coins!!")。如果你走过getChangeImpl的逻辑,这是有道理的,因为我们首先尝试绘制大硬币 - 在这种情况下是Quarter - 然后我们绘制一个,这样我们就有5 Dimes并且没有Quarters来弥补25 Cents。我把这个作为练习留给读者。