我想实现一台自动咖啡自动售货机。机器预先装满了硬币(例如10个5美分硬币,20个10美分硬币等)。
我希望用户输入一定数量(插入货币)减去咖啡的价格(例如120美分),然后根据自动售货机中可用的硬币给予更改。
我是这样开始的:
final int [] coins = {200, 100, 50, 20, 10, 5};
...但我怎么能给他们一个像10 = 5的值,因为有5个10美分的硬币?
答案 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 {...}
}
现在我们可以开始添加功能了。从简单开始,addCoins
和getCoinCount
功能听起来不错。
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 Quarter
和5 Dimes
,然后我们尝试绘制50
美分,它会抛出IllegalArgumentException("Insufficient coins!!")
。如果你走过getChangeImpl
的逻辑,这是有道理的,因为我们首先尝试绘制大硬币 - 在这种情况下是Quarter
- 然后我们绘制一个,这样我们就有5 Dimes
并且没有Quarters
来弥补25 Cents
。我把这个作为练习留给读者。