Divisibilità del dollaro e cambiamento perfetto


11

Ho $ 15 in tasca. Allo stesso modo, sono in un negozio che non dà il resto. Durante la navigazione, individuo un oggetto che costa $ 10 (tasse incluse). Posso acquistare quell'articolo senza perdere soldi?

In questo caso, la risposta è sì. Non importa come i miei $ 15 siano divisi (uno 10 e uno 5, o tre 5, o qualcos'altro), avrò sempre l'esatto $ 10 necessario.

Come secondo esempio, ho $ 0,16 in tasca. Quali altre somme di denaro devo essere in grado di pagare esattamente?

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

E se avessi $ 0,27 in tasca?

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

Nel caso sopra, c'erano solo poche somme di denaro per le quali avrei sempre avuto un cambiamento perfetto.

Il tuo compito

Scrivi il programma più breve (o la funzione denominata) che prende A) un importo intero di denaro e B) un elenco di possibili tagli come input e genera un elenco degli importi di denaro per i quali devo avere un cambiamento perfetto. L'input può essere STDIN o argomenti per il programma o la funzione. Non sarò super rigoroso nella formattazione dell'input; può corrispondere al modo in cui la tua lingua formatta gli array.

Forse una spiegazione più dettagliata

Ho una certa quantità di denaro in tasca, che è formata da una serie di possibili dimostrazioni di valuta. Se ho $ 8 e so che le possibili denominazioni sono $ 2 e $ 3, allora ci sono solo tante diverse combinazioni di banconote che potrebbero essere nella mia tasca. Questi sono 2+2+2+2e 3+3+2. Per essere in grado di produrre una quantità esatta di denaro, devo essere in grado di produrre quella quantità usando le sole fatture che sono in tasca. Se avessi quattro 2, potrei produrre 2, 4, 6, or 8. Se avessi due 3 e un 2, potrei produrre 2, 3, 5, 6, or 8Dal momento che non so quale di queste combinazioni ho effettivamente in tasca, la mia risposta finale è ridotta a 2, 6, 8. Questi sono i valori che so che potrei produrre dalla mia tasca, dato l'importo totale e le possibili denominazioni.

I / O di esempio calcolato a mano

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

Questo non è molto chiaro.
Motoku,

@FryAmTheEggman Ho aggiunto una "forse una spiegazione più dettagliata". Fammi sapere se è ancora confuso. (Ho anche rimosso un caso limite perché era praticamente inutile.)
PhiNotPi

Non vedo come stai 6 [2, 3, 4]. Non riesci 2+2+2a fare 3 e 3+3non a 2 e 4?
xnor

@xnor hai ragione, riparato.
PhiNotPi

Il programma dovrebbe funzionare in tempo ragionevole per tutti gli input? Ad esempio, la mia idea più breve è esponenziale nell'importo iniziale e 2 ^ 1500 è molto altro.
randomra,

Risposte:


2

Python 2, 200 197 193 140 byte

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}

(Grazie a @Nabb per i suggerimenti)

Ecco una soluzione scarsamente giocata per ora per iniziare le cose. Chiama con g(16, [1, 5, 10, 25])- l'output è un set con le denominazioni pertinenti.

L'approccio è semplice ed è suddiviso in due fasi:

  • fesamina tutti i modi per raggiungere ncon denominazioni D(ad es. [1, 5, 10]) e per ognuno elabora tutti gli importi che possono essere effettuati con tali denominazioni (ad es set([0, 1, 5, 6, 10, 11, 15, 16]).).
  • gcalcola le intersezioni dei risultati di f, quindi rimuove 0 per la risposta finale.

Il programma risolve i casi 1-5 e 7 bene, lo stack trabocca su 6 e impiega per sempre 8.

Se non esiste una soluzione (ad es. g(7, [2, 4, 6])), Il programma restituisce un set vuoto. Se è consentito generare un errore per un caso del genere, ecco un breve g:

g=lambda*a:reduce(set.__and__,f(*a))-{0}

g=lambda L,c=0:L and g(L[1:],c)|g(L,c+L.pop(0))or{c}è un po 'più corto
Nabb

Un po 'di più passando -{0}a ge usando [L]*-~ninvece di[L][-n:]
Nabb

1

JavaScript (ES6) 162 203 207

Modifica Modificato il modo di intersecare i set di risultati nell'array r. Un po 'più veloce, ma l'algoritmo puzza ancora.

Seguiranno spiegazioni più dettagliate.
In breve: c è una funzione ricorsiva che enumera tutte le possibili suddivisioni. k è una funzione ricorsiva che enumera tutte le possibili somme senza ripetizioni. Qualsiasi nuovo set di risultati trovato con la funzione k viene confrontato con il set precedente trovato, vengono conservati solo i risultati comuni.

Perché è così lento? Dover gestire un target totale di, diciamo, 1500 e un singolo pezzo di valore 1, elencando tutte le possibili somme non è una buona idea.

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

Ungolfed

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
}

Test nella console Firefox / FireBug

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

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

(tempo 84 msec)

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

[1, 2, 25, 26, 27]

(tempo 147252 msec, quindi non troppo veloce)


0

Wolfram Methematica, 104 byte

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

Ungolfed (letto dalla fine):

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
                ]&
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.