web-dev-qa-db-fra.com

Le nombre minimum de pièces dont la somme est S

Soit une liste de N pièces, leurs valeurs (V1, V2, ..., VN) et la somme totale S. Trouvez le nombre minimum de pièces dont la somme est S (on peut utiliser autant de pièces d'un type que nous voulons) ou signalons qu’il n’est pas possible de sélectionner des pièces de telle manière qu’elles totalisent un S. 

J'essaie de comprendre la programmation dynamique, je ne l'ai pas compris. Je ne comprends pas l'explication donnée, alors peut-être pourriez-vous me donner quelques astuces pour programmer cette tâche? Pas de code, juste des idées par lesquelles je devrais commencer.

Merci.

17
good_evening

C’est un problème classique de Knapsack, jetez un coup d’œil ici pour plus d’informations: Problème de Knapsack dans Wikipedia

Vous devriez également envisager un tri, en particulier un tri des valeurs les plus grandes aux plus petites.

6
kyndigs

La réponse précise à ce problème est bien expliquée ici. http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg

11
satarasu

Comme déjà indiqué, la programmation dynamique convient le mieux à ce problème. J'ai écrit un programme Python pour cela: -

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)

Pour entrée:

12 2 3 5

La sortie serait:

[0, -1, 1, 1, 2, 1, 2, 2, 2, 3, 2, 3, 3] 3 Le list_index est le total nécessaire et la valeur à list_index est le no. de pièces nécessaires pour obtenir ce total. La réponse pour l'entrée ci-dessus (obtenir une valeur 12) est 3 (pièces de monnaie de valeurs 5, 5, 2).

3
Barun Sharma

Je pense que l'approche que vous voulez est la suivante:

Vous savez que vous voulez produire une somme S. Les seuls moyens de produire S sont d’abord de produire S-V1, puis d’ajouter une pièce de valeur V1; ou pour produire S-V2 puis ajouter une pièce de valeur V2; ou...

T=S-V1 est à son tour reproductible à partir de T-V1, ou T-V2, ou ...

En faisant un pas en arrière de cette manière, vous pouvez déterminer le meilleur moyen, le cas échéant, de produire S à partir de votre Vs. 

2
Chowlett

La question a déjà reçu une réponse, mais je voulais fournir le code de travail en C que j'avais écrit, si cela pouvait aider quelqu'un. enter code here

Le code a une entrée codée en dur, mais c'est juste pour rester simple. La solution finale est le tableau min contenant le nombre de pièces nécessaires pour chaque somme. 

#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(); 
}
1
Gaurav Sinha

Pour une solution récursive rapide, vous pouvez vérifier ce lien: solution Java

Je suis en train de passer à travers les étapes minimales requises pour trouver la combinaison de pièces parfaite. Disons que nous avons coins = [20, 15, 7] et monetaryValue = 37. Ma solution fonctionnera comme suit:

[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!
0
Lucio

Je ne connais pas la programmation dynamique, mais voici comment je le ferais. Commencez à partir de zéro et continuez jusqu'à S. Produisez un ensemble avec une pièce, puis avec cet ensemble, créez un ensemble de deux pièces, et ainsi de suite ... Recherchez S et ignorez toutes les valeurs supérieures à S. Rappelez-vous le nombre de pièces utilisées pour chaque valeur.

0
Dialecticus
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);
    }

Considérez i-ème pièce. Soit il sera inclus ou non. Si elle est incluse, la somme de la valeur est diminuée de la valeur de la pièce et le nombre de pièces utilisées augmente de 1. Si elle n'est pas incluse, nous devons explorer les pièces restantes de la même manière. Nous prenons le minimum de deux cas. 

0
Mostafizar

L'idée principale est la suivante: pour chaque pièce j, la valeur [j] <= i (c'est-à-dire la somme) correspond au nombre minimal de pièces trouvé pour la i-valeur [j] (disons m) la somme (trouvée précédemment). Si m + 1 est inférieur au nombre minimum de pièces déjà trouvé pour la somme actuelle i, nous mettons à jour le nombre de pièces du tableau.

Pour ex-somme = 11 n = 3 et valeur [] = {1,3,5}
Voici le résultat obtenu

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 

Comme nous pouvons le constater pour la somme i = 3, 5, 8 et 10, nous améliorons notre précédent minimum de la manière suivante: 

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.  

Donc pour une somme de 11, nous aurons une réponse de 3 (5 + 5 + 1). 

Voici le code en C. Son semblable au pseudocode donné dans la page topcoder dont la référence est fournie dans l’une des réponses ci-dessus. 

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];
}
0
learner

Je savais que c'était une vieille question, mais que c'était une solution en 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);
    }
}
0
Bahador Biglari

Beaucoup de gens ont déjà répondu à la question. Voici un code qui utilise 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;
}
0
Sergey