Наименее монетный алгоритм, используемый Java

1

Я застрял в проблеме деноминации монет.

Я пытаюсь найти самое низкое количество монет, используемых для составления $ 5,70 (или 570 центов). Например, если массив монет {100,5,2,5,1} (100 x 10c монеты, 5 x 20c, 2 x 50c, 5 x $ 1 и 1 x $ 2 монеты), тогда результат должен быть { 0,1,1,3,1} В настоящий момент массив монет будет состоять из одинаковых наименований ($ 2, $ 1, 50c, 20c, 10c)

public static int[] makeChange(int change, int[] coins) {

    // while you have coins of that denomination left and the total
    // remaining amount exceeds that denomination, take a coin of that
    // denomination (i.e add it to your result array, subtract it from the
    // number of available coins, and update the total remainder). –

    for(int i= 0; i< coins.length; i++){
    while (coins[i] > 0) {

        if (coins[i] > 0 & change - 200 >= 0) {

            coins[4] = coins[4]--;
            change = change - 200;

        } else 

        if (coins[i] > 0 & change - 100 >= 0) {

            coins[3] = coins[3]--;
            change = change - 100;

        } else

        if (coins[i] > 0 & change - 50 >= 0) {

            coins[2] = coins[2]--;
            change = change - 50;

        } else

        if (coins[i] > 0 & change - 20 >= 0) {

            coins[1] = coins[1]--;
            change = change - 20;

        } else

        if (coins[i] > 0 & change - 10 >= 0) {

            coins[0] = coins[0]--;
            change = change - 10;

        }
    }

    }
    return coins;

}

Я зациклился на том, как вычесть значения из массива монет и вернуть их.

EDIT: новый код

  • 0
    Вот (неоптимизированный) алгоритм: для каждой купюры, пока у вас есть монеты этой купюры, а общая оставшаяся сумма превышает эту купюру, возьмите монету этой купюры (т.е. добавьте ее в массив результатов, вычтите ее из числа Доступные монеты, а также обновление общего остатка).
  • 1
    Вы не в правильном направлении, у вас есть проблема, которая является NP-Complete, и нет никакого известного полиномиального решения ее. Однако для случая, когда у вас есть бесконечность каждой монеты, существует псевдополиномиальное решение, и это известно как проблема создания изменений . Я не знаю, можете ли вы отрегулировать псевдополиномиальное решение для ограниченного количества каждой монеты.
Показать ещё 10 комментариев
Теги:
int

3 ответа

1

Ссылка wikipedia неясна в деталях о том, как решить, будет ли жадный алгоритм, такой как ваш, работать. Лучшая ссылка связана в этом вопросе CS StackExchange. По сути, если система монет каноническая, жадный алгоритм обеспечит оптимальное решение. Итак, [1, 2, 5, 10, 20] канонический? (используя 10 центов для единиц, так что последовательность начинается с 1)

Согласно этой статье, 5-монетная система неканоническая тогда и только тогда, когда она удовлетворяет точно одному из следующих условий:

  • [1, c2, c3] является неканоническим (false для [1, 2, 5])
  • он не может быть записан как [1, 2, c3, c3 + 1, 2 * c3] (true для [1, 2, 5, 10, 20])
  • greedyAnswerSize ((k + 1) * c4)> k + 1 с k * c4 <c5 <(k + 1) * c4; в этом случае для этого потребовалось бы ak * 10 <20 <(k + 1) * 10; в этом диапазоне нет целого числа k, поэтому для [1, 2, 5, 10, 20] это неверно.

Поэтому, поскольку жадный алгоритм не дает оптимальных ответов (и даже если бы он это сделал, я сомневаюсь, что он будет работать с ограниченными монетами), вам следует попробовать динамическое программирование или некоторое просвещенное обратное отслеживание:

import java.util.HashSet;
import java.util.PriorityQueue;

public class Main {

    public static class Answer implements Comparable<Answer> {
        public static final int coins[] = {1, 2, 5, 10, 20};

        private int availableCoins[] = new int[coins.length];
        private int totalAvailable;
        private int totalRemaining;
        private int coinsUsed;

        public Answer(int availableCoins[], int totalRemaining) {
            for (int i=0; i<coins.length; i++) {
                this.availableCoins[i] = availableCoins[i];
                totalAvailable += coins[i] * availableCoins[i];
            }
            this.totalRemaining = totalRemaining;
        }

        public boolean hasCoin(int coinIndex) { 
            return availableCoins[coinIndex] > 0; 
        }

        public boolean isPossibleBest(Answer oldBest) {
            boolean r = totalRemaining >= 0
                && totalAvailable >= totalRemaining
                && (oldBest == null || oldBest.coinsUsed > coinsUsed);
            return r;
        }

        public boolean isAnswer() {
            return totalRemaining == 0;
        }

        public Answer useCoin(int coinIndex) {
            Answer a = new Answer(availableCoins, totalRemaining - coins[coinIndex]);
            a.availableCoins[coinIndex]--;
            a.totalAvailable = totalAvailable - coins[coinIndex];
            a.coinsUsed = coinsUsed+1;
            return a;
        }

        public int getCoinsUsed() {
            return coinsUsed;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder("{");
            for (int c : availableCoins) sb.append(c + ",");            
            sb.setCharAt(sb.length()-1, '}');
            return sb.toString();
        }

        // try to be greedy first
        @Override
        public int compareTo(Answer a) {
            int r = totalRemaining - a.totalRemaining;
            return (r==0) ? coinsUsed - a.coinsUsed : r;
        }
    }        

    // returns an minimal set of coins to solve
    public static int makeChange(int change, int[] availableCoins) {
        PriorityQueue<Answer> queue = new PriorityQueue<Answer>();
        queue.add(new Answer(availableCoins, change));
        HashSet<String> known = new HashSet<String>();
        Answer best = null;
        int expansions = 0;
        while ( ! queue.isEmpty()) {
            Answer current = queue.remove();            
            expansions ++;
            String s = current.toString();
            if (current.isPossibleBest(best) && ! known.contains(s)) {
                known.add(s);
                if (current.isAnswer()) {
                    best = current;
                } else {
                    for (int i=0; i<Answer.coins.length; i++) {
                        if (current.hasCoin(i)) {
                            queue.add(current.useCoin(i));
                        }
                    }
                }
            }
        }
        // debug
        System.out.println("After " + expansions + " expansions");
        return (best != null) ? best.getCoinsUsed() : -1;
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            System.out.println("Solving for " + i + ":"
                + makeChange(i, new int[]{100,5,2,5,1}));
        }
    }
}
1

Решение грубой силы состоит в том, чтобы опробовать доступное количество монет с наивысшим наименованием (прекращение, когда вы закончите, или сумма станет отрицательной), и для каждого из этих рекурсоров при решении оставшейся суммы с более коротким списком, который исключает эту деноминацию, и выберите минимум из них. Если базовый случай равен 1c, проблема всегда может быть решена, а базовый случай - return n иначе n/d0 (d0 представляющий наименьшее деноминацию), но необходимо позаботиться о том, чтобы вернуть большое значение, когда оно равномерно делится, поэтому оптимизация может выбрать другую ветку. Воспоминание возможно и параметризуется оставшейся суммой и следующим наименованием. Таким образом, размер таблицы заметок будет равен O(n*d), где n - начальное количество, а d - количество наименований.

Таким образом, проблема может быть решена в псевдополиномиальном времени.

0

Вы находитесь в неправильном направлении. Эта программа не даст вам оптимального решения. Чтобы получить оптимальное решение, выполните динамические алгоритмы, реализованные и обсуждаемые здесь. Посетите эти несколько ссылок:

  1. ссылка 1
  2. ссылка 2
  3. ссылка 3
  • 1
    Эти ссылки решают другую проблему. Здесь количество монет каждого вида ограничено.
  • 0
    Я посоветовал ему просто взглянуть на эти ссылки, чтобы получить представление. Я не сказал, что для его проблемы есть код ответа. Голосование вниз, его ....
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню