Divisibilité du dollar et changement parfait

11

J'ai 15 $ en poche. De même, je suis dans un magasin qui ne donne pas de monnaie. Pendant la navigation, je repère un article qui coûte 10 $ (taxes incluses). Puis-je acheter cet article sans perdre d'argent?

Dans ce cas, la réponse est oui. Peu importe la façon dont mes 15 $ sont répartis (un 10 et un 5, ou trois 5, ou autre), j'aurai toujours les 10 $ nécessaires.

Comme deuxième exemple, j'ai 0,16 $ en poche. Quelles autres sommes d'argent dois-je pouvoir payer exactement?

Possible Divisions:
0.01, 0.05, 0.10
0.01, 0.05 x 3
0.01 x 16
Guaranteed Exact Change:
0.01, 0.05, 0.06, 0.10, 0.11, 0.15, 0.16

Et si j'ai 0,27 $ en poche?

Possible Divisions:
0.01 x 2, 0.25
0.01 x 2, 0.05, 0.10 x 2
0.01 x 2, 0.05 x 3, 0.10
0.01 x 2, 0.05 x 5
0.01 x 27
Guaranteed Exact Change:
0.01, 0.02, 0.25, 0.26, 0.27

Dans le cas ci-dessus, il n'y avait que quelques sommes d'argent pour lesquelles j'aurais toujours une monnaie parfaite.

Ta tâche

Écrivez le programme le plus court (ou la fonction nommée) qui prend A) un montant entier d'argent et B) une liste de dénominations possibles en entrée et génère une liste des montants d'argent pour lesquels je dois avoir un changement parfait. L'entrée peut être STDIN ou des arguments pour le programme ou la fonction. Je ne vais pas être super strict sur le formatage d'entrée; il peut correspondre à la façon dont vos tableaux de formats de langue.

Peut-être une explication plus détaillée

J'ai une certaine somme d'argent dans ma poche, qui est formée d'un ensemble de démonstrations possibles de monnaie. Si j'ai 8 $ et que je sais que les dénominations possibles sont de 2 $ et 3 $, alors il n'y a que beaucoup de combinaisons de factures différentes qui pourraient être dans ma poche. Ce sont 2+2+2+2et 3+3+2. Afin de pouvoir produire une somme exacte, je dois pouvoir produire cette quantité en utilisant les seules factures qui sont dans ma poche. Si j'avais quatre 2, je pourrais produire 2, 4, 6, or 8. Si j'avais deux 3 et un 2, je pourrais produire 2, 3, 5, 6, or 8Puisque je ne sais pas laquelle de ces combinaisons j'ai réellement dans ma poche, ma réponse finale est réduite à 2, 6, 8. Ce sont les valeurs que je sais que je pourrais produire de ma poche, compte tenu du montant total et des dénominations possibles.

Exemple d'E / S calculées à la main

7 [3, 4]
3, 4, 7        //only one possible division into 3 + 4

7 [3, 2]
2, 3, 4, 5, 7  //the only division is 3 + 2 + 2

6 [2, 3, 4]
6     //divisions are 2+2+2, 3+3, 2+4 

16 [1, 5, 10, 25]          //this represents one of the examples above
1, 5, 6, 10, 11, 15, 16

27 [1, 5, 10, 25]          //another example from above
1, 2, 25, 26, 27

1500 [1, 5, 10, 25, 100, 500, 1000, 2000]
500, 1000, 1500

600 [100, 500, 1000, 2000]
100, 500, 600

600 [200, 1, 5, 10, 25, 100, 500, 1000, 2000]
600
PhiNotPi
la source
Ce n'est pas très clair.
motoku
@FryAmTheEggman J'ai ajouté "peut-être une explication plus détaillée". Faites-moi savoir si cela prête toujours à confusion. (J'ai également supprimé un cas de bord, car il était à peu près inutile.)
PhiNotPi
Je ne vois pas comment tu vas 6 [2, 3, 4]. Vous ne pouvez pas 2+2+2faire 3 et 3+3ne pas faire 2 et 4?
xnor
@xnor vous avez raison, corrigé.
PhiNotPi
Le programme doit-il s'exécuter dans un délai raisonnable pour toutes les entrées? Par exemple, mon idée la plus courte est exponentielle dans le montant de départ et 2 ^ 1500 est beaucoup de n'importe quoi.
randomra

Réponses:

2

Python 2, 200 197 193 193 140 octets

f=lambda n,D,S={0}:sum([f(n-x,D,S|{x+y for y in S})for x in D],[])if n>0else[S]*-~n
g=lambda*a:(f(*a)and reduce(set.__and__,f(*a))or{0})-{0}

(Merci à @Nabb pour les conseils)

Voici une solution mal jouée pour l'instant pour commencer. Appel avec g(16, [1, 5, 10, 25])- la sortie est un ensemble avec les dénominations pertinentes.

L'approche est simple et se décompose en deux étapes:

  • fexamine toutes les façons d'atteindre les ndénominations D(par exemple [1, 5, 10]), et pour chacune, il calcule tous les montants qui peuvent être réalisés avec ces dénominations (par exemple set([0, 1, 5, 6, 10, 11, 15, 16])).
  • gcalcule les intersections des résultats de f, puis supprime 0 pour la réponse finale.

Le programme résout très bien les cas 1 à 5 et 7, les débordements de pile sur 6 et dure indéfiniment sur 8.

S'il n'y a pas de solution (par exemple g(7, [2, 4, 6])), le programme renvoie un ensemble vide. Si une erreur est autorisée à être levée pour un tel cas, alors voici une courte g:

g=lambda*a:reduce(set.__and__,f(*a))-{0}
Sp3000
la source
g=lambda L,c=0:L and g(L[1:],c)|g(L,c+L.pop(0))or{c}est un peu plus court
Nabb
Un peu plus en passant -{0}à g et en utilisant [L]*-~nau lieu de[L][-n:]
Nabb
1

JavaScript (ES6) 162 203 207

Modifier Modification du mode d'intersection des jeux de résultats dans le tableau r. Un peu plus vite, mais l'algorithme pue toujours.

Une explication plus détaillée suivra.
En bref: c est une fonction récursive qui énumère toutes les subdivisions possibles. k est une fonction récursive qui énumère toutes les sommes possibles sans répétitions. Tout nouvel ensemble de résultats trouvé avec la fonction k est comparé à l'ensemble précédent trouvé, seuls les résultats communs sont conservés.

Pourquoi est-ce si lent? Devoir gérer un total cible de, disons, 1500 et un seul morceau de valeur 1, énumérer toutes les sommes possibles n'est pas une bonne idée.

F=(s,d,r,
  c=(s,i,t=[],v,k=(i,s,v)=>{for(;v=t[i++];)k(i,s+v);o[s]=s})=>
  {for(s||(i=k(o=[],0),r=(r||o).filter(v=>o[v]));v=d[i];++i)s<v||c(s-v,i,[...t,v])}
)=>c(s,0)||r

Non golfé

F=(s,d)=>{
  var r
  var c=(s,i,t=[])=>
  {
    var o=[],v
    var k=(i,s)=> // find all sums for the current list t, set a flag in the o array
    {
      var v
      for(;v=t[i++];)k(i,s+v)
      o[s]=s
    }

    if (s==0) {
      k(0,0)
      if (r)
        r = r.filter(v=>o[v]) // after first loop, intersect with current
      else
        r = o.filter(v=>v) // first loop, keep all results
    } 
    else
      for(;v=d[i];++i)
      { 
        if (s >= v) 
          c(s-v, i, t.concat(v))
      }
  }
  c(s,0) // enumerate all possible set of pieces
  return r
}

Tester dans la console Firefox / FireBug

F(16,[1,5,10,25])

[1, 5, 6, 10, 11, 15, 16]

(temps 84 ms)

F(27, [1, 5, 10, 25]) 

[1, 2, 25, 26, 27]

(temps 147252 ms, donc pas trop rapide)

edc65
la source
0

Wolfram Methematica, 104 octets

Rest@*Intersection@@Map[Total]/@Subsets/@Union[Sort/@IntegerPartitions[#,#,PadLeft[{},Length[#2]#,#2]]]&

Non golfé (lu de la fin):

Rest@* // Removing 0
  Intersection@@   // Intersecting all totals
     Map[Total]/@  // Counting total of each subset
        Subsets/@  // Getting all the subsets of each partition
           Union[  // Removing duplicates 
              Sort/@ // Sorting each partition (to remove duplicates next)
                 IntegerPartitions[#,#,PadLeft[{},Length[#2]#,#2]] // Getting all Integer partitions
                ]&
Savenkov Alexey
la source