#java #алгоритм
У меня было задание сделать программу, которая будет разменивать большую купюру на мелкие монеты. Это у меня худо-бедно получилось. Теперь добавилось условие: чтобы возвращались все возможные варианты размена. И тут я что-то поплыл. Как можно модифицировать код, чтобы получить не один вариант размена, а все? public interface Exchanging { Mapexchange(); } public class Exchange implements Exchanging { /** * Money which should be exchanging. */ private final int sumForExchange; /** * Nominal coins. */ private final int[] denominationСoins; /** * Default constructor. * * @param sumForExchange money which should be exchanging. * @param denominationСoins array with nominal coins. */ public Exchange(final int sumForExchange, final int... denominationСoins) { this.denominationСoins = denominationСoins; this.sumForExchange = sumForExchange; } /** * Exchange big money on less coins. * * @return map with coins. Nominal is key, amount coin is value. */ @Override public Map exchange() { Map result = new HashMap<>(); int[] coins = sort(); int residue = sumForExchange; for (int i = 0; i != coins.length; i++) { ExchangeByValue exchangeByValue = new ExchangeByValue( residue, coins[i]); // how much coins content current residue for denominationСoins[i]. int amountCoin = exchangeByValue.exchange(); result.put(coins[i], amountCoin); // update residue. residue = exchangeByValue.getResidue(); } return result; } /** * Sorting array with coins nominal. * * @return arr sort by descending. */ private int[] sort() { int[] result = denominationСoins; for (int i = result.length - 1; i >= 0; i--) { for (int j = 0; j < i; j++) { if (result[j] < result[j + 1]) { int temp = result[j]; result[j] = result[j + 1]; result[j + 1] = temp; } } } return result; } /** * Calculate residue and amount coins. */ private class ExchangeByValue { /** * Residue for division on coins. */ private final int denomination; /** * Nominal coin. */ private final int divider; /** * Default constructor. * * @param denomination Residue for division on coins. * @param divider Nominal coin. */ private ExchangeByValue(final int denomination, final int divider) { this.denomination = denomination; this.divider = divider; } /** * @return amount coins which may by get for this.denomination. */ private int exchange() { return denomination / divider; } /** * @return residue for future exchange. */ private int getResidue() { return denomination % divider; } } }
Ответы
Ответ 1
А у меня получилось такое решение: Listcoins = Arrays.asList(1, 2, 5); coinsChange(10, coins).forEach(System.out::println); реализация метода public static List > coinsChange(int countOfMoney, List
coins) { Collections.sort(coins); List > result = new ArrayList<>(); //из лямбды нельзя рекурсивно ссылаться на себя же :( class Wrapper
{ private T function; } Wrapper >>> recursion = new Wrapper<>(); recursion.function = money -> (numberOfCoin, buffer) -> { if (money < 0 || numberOfCoin < 0) return; if (money == 0) { result.add(buffer); return; } recursion.function.apply(money).accept(numberOfCoin - 1, new ArrayList<>(buffer)); int coin = coins.get(numberOfCoin); buffer = new ArrayList<>(buffer); buffer.add(coin); recursion.function.apply(money - coin).accept(numberOfCoin, buffer); }; recursion.function.apply(countOfMoney).accept(coins.size() - 1, new ArrayList<>()); return result; } UPD обновил решение, попытался переписать в функциональном стиле, насколько это позволила мне сделать java Ответ 2
Решение, представленное на Хабре, заключается в том, что количество вариантов набора текущей суммы - это сумма количества вариантов набора меньших сумм, из которых можно получить текущую сумму "докидыванием" одной монеты. Причем номинал монеты должен быть не меньше номиналов монет, входящих в варианты меньших сумм, дабы, например, вариант 1 1 5 не учитывался ещё и как 1 5 1 и 5 1 1. Сохранение всех вариантов ощутимо усложняет код. В итоге получается так: Way.java для хранения конкретного варианта размена: public class Way { private final Integer[] coins; public Way() { coins = new Integer[0]; } public Way(Way way) { coins = new Integer[way.coins.length + 1]; System.arraycopy(way.coins, 0, coins, 0, way.coins.length); } public void add(int coin) { coins[coins.length - 1] = coin; } public void print() { for (int i = 0; i < coins.length - 1; i++) { System.out.print(coins[i] + " "); } System.out.println(); } } WaysGroup.java для хранения групп вариантов размена: import java.util.*; public class WaysGroup { private final Listways; public WaysGroup() { ways = new ArrayList<>(); } public void add(Way way) { ways.add(way); } public void add(int coin) { for (Way way : ways) { way.add(coin); } } public void add(WaysGroup group) { if (group == null) { return; } for (Way way : group.ways) { ways.add(new Way(way)); } } public void print() { for (Way way : ways) { way.print(); } } } Сам алгоритм в Exchange.java: public WaysGroup getAllExchanges() { int[] coins = denominationСoins; WaysGroup[][] waysGroups = new WaysGroup[coins.length][sumForExchange + 1]; waysGroups[0][0] = new WaysGroup(); waysGroups[0][0].add(new Way()); for (int i = 0; i < sumForExchange; i++) { for (int j = 0; j < coins.length; j++) { for (int k = j; k < coins.length; k++) { if (i + coins[k] <= sumForExchange) { if (waysGroups[k][i + coins[k]] == null) { waysGroups[k][i + coins[k]] = new WaysGroup(); } waysGroups[k][i + coins[k]].add(waysGroups[j][i]); } } if (i + coins[j] <= sumForExchange) { waysGroups[j][i + coins[j]].add(coins[j]); } waysGroups[j][i] = null; } } WaysGroup result = new WaysGroup(); for (int i = 0; i < coins.length; i++) { result.add(waysGroups[i][sumForExchange]); } return result; } И, наконец, небольшой тест: public static void main(String[] args) { Exchange exchange = new Exchange(10, 1, 2, 3); WaysGroup allWays = exchange.getAllExchanges(); allWays.print(); } Код рассчитан на вывод значений на экран, однако вполне можно добавить getter-ы в Way и WaysGroup для работы с полученными списками монет. Проведённые оптимизации, которые, возможно, делают код менее понятным: WaysGroup создаются только при необходимости и удаляются сразу же, как только перестают быть нужными. Это сильно уменьшает затраты памяти и ускоряет работу примерно вдвое. В Way используется Integer[] вместо ArrayList . Благодаря более быстрой обработке public void add(int coin) это ускоряет работу при больших значениях в несколько раз. Ответ 3
Еще один вариант: import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class CashMachine { private final int[] values; public CashMachine(final int[] values) { this.values = values; Arrays.sort(this.values); } public List> exchange(int note) { return exchange(note, this.values[this.values.length - 1]); } private List
> exchange(int note, int maxCoin) { List
> result = new ArrayList<>(); if (note == 0) { result.add(new ArrayList<>()); } else { for (int i = this.values.length - 1; i >= 0; i--) { int coin = values[i]; if (coin > note || coin > maxCoin) { continue; } for (List
remain : exchange(note - coin, coin)) { List set = new ArrayList<>(); set.add(coin); set.addAll(remain); result.add(set); } } } return result; } }
Комментариев нет:
Отправить комментарий