LINUX.ORG.RU

Прогуливал в школе рекурсию

 


0

2

Поможите понять.

Есть задача. Дано: вектор с монетами разных достоинств coins и кол-во денег amount. Надо вернуть наименьшее кол-во монет, которым можно выразить данное кол-во денег.

И решение, которого я решительно не понимаю. Объясните на пальцах, как это работает.

int help(vector<int>& coins, int amount, int n)
{
    if(amount == 0)
        return 0;

    if(n < 0 || amount < 0)
        return INT_MAX-1;

    int one = help(coins, amount, n-1);
    int two = 1 + help(coins, amount-coins[n], n);

    return min(one, two);
}

int coinChange(vector<int>& coins, int amount)
{
    int ans = help(coins, amount, coins.size()-1);

    return (ans < INT_MAX-1) ? ans : -1;
}

Почему в тегах си, а код на цепепе?

И решение, которого я решительно не понимаю.

Что ты не понимаешь? Конкретное место в коде или алгоритм? Называй место или иди читать CLRS.

utf8nowhere ★★★
()

one – количество монет для суммы, если последнюю не использовать

two – берём последнюю, вычитаем её значение из суммы, возвращаем 1 + количество монет для оставшейся суммы.

Возвращаем минимальное из двух.

monk ★★★★★
()

Перебирают монетки по одной с конца и смотрят что получится если их включать / не включать в платёж.

bugfixer ★★★★★
()

Кажется, тут ошибка:

-int two = 1 + help(coins, amount-coins[n], n);
+int two = 1 + help(coins, amount-coins[n], n-1);

А так идея следующая - начиная с последней монетки, мы проверяем что будет если её не брать (тогда задача сводится к самой себе на множестве монеток кроме последней), или если брать (тогда задача сводится к самой себе на множестве монеток кроме последней, суммой уменьшенной на номинал взятой монеты и результатом увеличенным на единицу - это посчитали взятую монету). Из двух результатов выбираем по условию минимальный. В условиях проверяются граничные условия - перебрали все монетки или взяли монеток больше чем нужная сумма).

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от slovazap

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

mky ★★★★★
()
Ответ на: комментарий от DrBrown

ПыПыСы. Сначала хотел сказать что память раньше кончится чем это произойдёт… Но нет :) Хотя я думаю что при количестве монеток больше ~20 (может даже меньше) оно и так займёт forever.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Всё-таки функция help может вернуть INT_MAX - 1 в случае неуспеха.

DrBrown
()
Ответ на: комментарий от mky

two — это мы пытаемся набрать сумму используя все монеты, one — это пытаемся набрать сумму убрая одну из монет.

Вот тут можно подробнее? Что значит «используя все монеты»? В каком конкретно случае подойдет one, а в каком two? Просил же на пальцах.

untitl3d
() автор топика
Ответ на: комментарий от slovazap

Можешь на конкретном примере показать, что происходит когда вызывается one и когда two? Зачем проверять, надо ли брать еще одну монету, если, допустим надо получить 10, мы берем 5 и 5 и получаем выход из рекурсии по первому if'у. Смысл проверять следующую?

untitl3d
() автор топика

Короче, что я не понимаю:

1. Почему в one amount остается тот же, а coins.size уменьшается?

2. Почему в two amount уменьшается, а coins.size остается тот же?

untitl3d
() автор топика
Ответ на: комментарий от untitl3d

Можешь на конкретном примере показать, что происходит когда вызывается one и когда two?

На конкретном примере прогнать этот код на бумажке можешь ты сам.

Зачем проверять, надо ли брать еще одну монету, если, допустим надо получить 10, мы берем 5 и 5 и получаем выход из рекурсии по первому if’у. Смысл проверять следующую?

Мы не проверяем следующую, мы выходим из рекурсии по if’у.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

В two мы берем одну и ту же монету несколько раз, пока не будет overflow, а когда он будет, берем one, так? Жадный алгоритм?

untitl3d
() автор топика
Ответ на: комментарий от untitl3d

two инициализируется вызовом help, где рассматриваются оба варианта: one и two. Это не называется «жадный алгоритм». Жадный алгоритм для размена выглядит по-другому.

utf8nowhere ★★★
()
Ответ на: комментарий от untitl3d

Я же привел ссылку на текстовое описание.

Мы в этой программе перебираем все варианты. Вот у нас есть монета в конце вектора в 50 коп, мы пробуем набрать сумму без использования этой монеты (one — уменьшили рассматриваемое число элементов вектора) и с использованием этой монеты (two — для суммы на 50 коп. меньше и с +1 к числу монет).

mky ★★★★★
()
Ответ на: комментарий от slovazap

Конечно не может. Есть оригинальный код, решающий одну задачу и ваш патченый, решающий другую задачу.

-1 либо и в one и в two, либо нигде.

Вы сначала напишите, что лежит в векторе — монетки или их номиналы при бесконечном монеток каждого номинала.

mky ★★★★★
()
Ответ на: комментарий от mky

Мне кажется, по ссылке немного другой алгоритм. А какой смысл в

мы пробуем набрать сумму без использования этой монеты

?

untitl3d
() автор топика
Ответ на: комментарий от untitl3d

Если всегда вычитается последняя монета, то единственный способ попробовать повычитать другие номиналы — это выкинуть последний.

Обычно задача размена решается более другим (менее извращённым) способом. Когда из текущей суммы вычитается каждый доступный номинал, и берётся минимум от получившихся подзадач.

utf8nowhere ★★★
()
Последнее исправление: utf8nowhere (всего исправлений: 1)

Было бы решение на Rust, еще можно было бы что-то понять. А этот ваш богомерзкий си просто омерзителен.

Virtuos86 ★★★★★
()
Ответ на: комментарий от mky

Есть оригинальный код, решающий одну задачу

Оригинальный код не решает ни одну задачу из упомянутых. Для одной задачи ошибка будет в строчке с one, для другой - в строчке с two.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Оригинальный код не решает ни одну задачу из упомянутых.

Почему он не решает задачу с неограниченным числом монет (в векторе — номиналы)? Можно контрпример?

utf8nowhere ★★★
()
Ответ на: комментарий от untitl3d

Там же в ссылке пример приведён. У нас есть 1,5,10,21,25 центов. Нам нужно 63 цента. Если мы используем 25 центов, то никак не получится набрать 63 цента ТРЕМЯ монетами (3x21). И это уже к рекурсии отношения не имеет.

mky ★★★★★
()
Ответ на: комментарий от slovazap

Оригинальный код решает задачу. Мы либо вобще не берём монету (строка one), либо берём и допускаем взять такую же монету ещё раз (строка two).

А для какой задачи ошибка в строчке с one?

mky ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.