Ответ 1
Это классическая проблема с рюкзаком, посмотрите здесь дополнительную информацию: Проблема с рюкзаком Википедии
Вы также должны посмотреть на некоторую сортировку, в частности сортировку от самых больших до наименьших.
Учитывая список из N монет, их значения (V1, V2,..., VN) и общая сумма S. Найдите минимальное количество монет, сумма которых равна S (мы можем использовать столько монет один тип, который мы хотим) или сообщить, что невозможно выбрать монеты таким образом, чтобы они суммировались с S.
Я пытаюсь понять динамическое программирование, не понял. Я не понимаю данное объяснение, так что, может быть, вы можете бросить мне несколько советов, как программировать эту задачу? Нет кода, просто идеи, с которых я должен начать.
Спасибо.
Это классическая проблема с рюкзаком, посмотрите здесь дополнительную информацию: Проблема с рюкзаком Википедии
Вы также должны посмотреть на некоторую сортировку, в частности сортировку от самых больших до наименьших.
Точный ответ на эту проблему хорошо объяснен здесь. http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg
Как уже указывалось, динамическое программирование лучше всего подходит для этой проблемы. Я написал программу Python для этого: -
def sumtototal(total, coins_list):
s = [0]
for i in range(1, total+1):
s.append(-1)
for coin_val in coins_list:
if i-coin_val >=0 and s[i-coin_val] != -1 and (s[i] > s[i-coin_val] or s[i] == -1):
s[i] = 1 + s[i-coin_val]
print s
return s[total]
total = input()
coins_list = map(int, raw_input().split(' '))
print sumtototal(total, coins_list)
Для ввода:
12
2 3 5
Вывод будет:
[0, -1, 1, 1, 2, 1, 2, 2, 2, 3, 2, 3, 3]
3
List_index - это общая сумма, а значение в list_index - нет. монет, необходимых для получения этой суммы. Ответ для вышеприведенного ввода (получение значения 12) равен 3 (монеты значений 5, 5, 2).
Я думаю, что подход, который вы хотите, выглядит следующим образом:
Вы знаете, что хотите создать сумму S
. Единственные способы создания S
- сначала произвести S-V1
, а затем добавить монету значения V1
; или произвести S-V2
, а затем добавить монету значения V2
; или...
В свою очередь, T=S-V1
можно получить из T-V1
или T-V2
, или...
Отступив таким образом, вы можете определить лучший способ, если таковой имеется, создать S
из вашего V
s.
Вопрос уже ответил, но я хотел предоставить рабочий код C, который я написал, если он кому-то помогает. enter code here
Код имеет жестко закодированный ввод, но просто сохранить его просто. Конечным решением является массив min, содержащий количество монет, необходимых для каждой суммы.
#include"stdio.h"
#include<string.h>
int min[12] = {100};
int coin[3] = {1, 3, 5};
void
findMin (int sum)
{
int i = 0; int j=0;
min [0] = 0;
for (i = 1; i <= sum; i++) {
/* Find solution for Sum = 0..Sum = Sum -1, Sum, i represents sum
* at each stage */
for (j=0; j<= 2; j++) {
/* Go over each coin that is lesser than the sum at this stage
* i.e. sum = i */
if (coin[j] <= i) {
if ((1 + min[(i - coin[j])]) <= min[i]) {
/* E.g. if coin has value 2, then for sum i = 5, we are
* looking at min[3] */
min[i] = 1 + min[(i-coin[j])];
printf("\nsetting min[%d] %d",i, min[i]);
}
}
}
}
}
void
initializeMin()
{
int i =0;
for (i=0; i< 12; i++) {
min[i] = 100;
}
}
void
dumpMin()
{
int i =0;
for (i=0; i< 12; i++) {
printf("\n Min[%d]: %d", i, min[i]);
}
}
int main()
{
initializeMin();
findMin(11);
dumpMin();
}
Я не знаю о динамическом программировании, но именно так я и сделал бы это. Начните с нуля и пройдите к S
. Произведите набор с одной монетой, затем с этим набором создайте набор из двух монет и так далее... Найдите S
и проигнорируйте все значения, превышающие S
. Для каждого значения помните количество используемых монет.
Многие люди уже ответили на вопрос. Вот код, который использует DP
public static List<Integer> getCoinSet(int S, int[] coins) {
List<Integer> coinsSet = new LinkedList<Integer>();
if (S <= 0) return coinsSet;
int[] coinSumArr = buildCoinstArr(S, coins);
if (coinSumArr[S] < 0) throw new RuntimeException("Not possible to get given sum: " + S);
int i = S;
while (i > 0) {
int coin = coins[coinSumArr[i]];
coinsSet.add(coin);
i -= coin;
}
return coinsSet;
}
public static int[] buildCoinstArr(int S, int[] coins) {
Arrays.sort(coins);
int[] result = new int[S + 1];
for (int s = 1; s <= S; s++) {
result[s] = -1;
for (int i = coins.length - 1; i >= 0; i--) {
int coin = coins[i];
if (coin <= s && result[s - coin] >= 0) {
result[s] = i;
break;
}
}
}
return result;
}
Основная идея - для каждой монеты j значение [j] <= я (т.е. сумма), мы смотрим на минимальное количество монет, найденных для суммы i-значения [j] (пусть m) (ранее найдено). Если m + 1 меньше минимального количества монет, уже найденных для текущей суммы i, тогда мы обновляем количество монет в массиве.
Для ex - sum = 11 n = 3 и значение [] = {1,3,5}
Ниже мы получаем результат
i- 1 mins[i] - 1
i- 2 mins[i] - 2
i- 3 mins[i] - 3
i- 3 mins[i] - 1
i- 4 mins[i] - 2
i- 5 mins[i] - 3
i- 5 mins[i] - 1
i- 6 mins[i] - 2
i- 7 mins[i] - 3
i- 8 mins[i] - 4
i- 8 mins[i] - 2
i- 9 mins[i] - 3
i- 10 mins[i] - 4
i- 10 mins[i] - 2
i- 11 mins[i] - 3
Как мы можем наблюдать для сумм я = 3, 5, 8 и 10, мы улучшаем наш предыдущий минимум следующими способами:
sum = 3, 3 (1+1+1) coins of 1 to one 3 value coin
sum = 5, 3 (3+1+1) coins to one 5 value coin
sum = 8, 4 (5+1+1+1) coins to 2 (5+3) coins
sum = 10, 4 (5+3+1+1) coins to 2 (5+5) coins.
Итак, для суммы = 11 мы получим ответ как 3 (5 + 5 + 1).
Вот код в C. Его аналогично псевдокоду, указанному на странице topcoder, ссылка на которую приведена в одном из приведенных выше ответов.
int findDPMinCoins(int value[], int num, int sum)
{
int mins[sum+1];
int i,j;
for(i=1;i<=sum;i++)
mins[i] = INT_MAX;
mins[0] = 0;
for(i=1;i<=sum;i++)
{
for(j=0;j<num;j++)
{
if(value[j]<=i && ((mins[i-value[j]]+1) < mins[i]))
{
mins[i] = mins[i-value[j]] + 1;
printf("i- %d mins[i] - %d\n",i,mins[i]);
}
}
}
return mins[sum];
}
int getMinCoins(int arr[],int sum,int index){
int INFINITY=1000000;
if(sum==0) return 0;
else if(sum!=0 && index<0) return INFINITY;
if(arr[index]>sum) return getMinCoins(arr, sum, index-1);
return Math.min(getMinCoins(arr, sum, index-1), getMinCoins(arr, sum-arr[index], index-1)+1);
}
Рассмотрим i-ю монету. Либо он будет включен, либо нет. Если он включен, то сумма значения уменьшается на величину монеты, а количество используемых монет увеличивается на 1. Если оно не включено, нам нужно исследовать оставшиеся монеты аналогично. Мы берем минимум два случая.
Я знал, что это старый вопрос, но это решение на Java.
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class MinCoinChange {
public static void min(int[] coins, int money) {
int[] dp = new int[money + 1];
int[] parents = new int[money + 1];
int[] usedCoin = new int[money + 1];
Arrays.sort(coins);
Arrays.fill(dp, Integer.MAX_VALUE);
Arrays.fill(parents, -1);
dp[0] = 0;
for (int i = 1; i <= money; ++i) {
for (int j = 0; j < coins.length && i >= coins[j]; ++j) {
if (dp[i - coins[j]] + 1 < dp[i]) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
parents[i] = i - coins[j];
usedCoin[i] = coins[j];
}
}
}
int parent = money;
Map<Integer, Integer> result = new HashMap<>();
while (parent != 0) {
result.put(usedCoin[parent], result.getOrDefault(usedCoin[parent], 0) + 1);
parent = parents[parent];
}
System.out.println(result);
}
public static void main(String[] args) {
int[] coins = { 1, 5, 10, 25 };
min(coins, 30);
}
}
Для быстрого рекурсивного решения вы можете проверить эту ссылку: java-решение
Я выполняю минимальные шаги, необходимые для нахождения идеальной комбинации монет. Скажем, у нас есть coins = [20, 15, 7]
и monetaryValue = 37
. Мое решение будет работать следующим образом:
[20] -> sum of array bigger than 37? NO -> add it to itself
[20, 20] greater than 37? YES (20 + 20) -> remove last and jump to smaller coin
[20, 15] 35 OK
[20, 15, 15] 50 NO
[20, 15, 7] 42 NO
// Replace biggest number and repeat
[15] 15 OK
[15, 15] 30 OK
[15, 15, 15] 45 NO
[15, 15, 7] 37! RETURN NUMBER!
def leastCoins(lst, x):
temp = []
if x == 0:
return 0
else:
while x != 0:
if len(lst) == 0:
return "Not Possible"
if x % max(lst) == 0:
temp.append((max(lst), x//max(lst)))
x = 0
elif max(lst) < x:
temp.append((max(lst), x//max(lst)))
x = x % max(lst)
lst.remove(max(lst))
else:
lst.remove(max(lst))
return dict(temp)
наименьшие монеты ([17,18,2], 100652895656565)