Divisibilidad en dólares y cambio perfecto

11

Tengo $ 15 en mi bolsillo. Del mismo modo, estoy en una tienda que no da cambio. Mientras navego, veo un artículo que cuesta $ 10 (impuestos incluidos). ¿Puedo comprar ese artículo sin perder dinero?

En este caso, la respuesta es sí. No importa cómo se dividan mis $ 15 (uno 10 y uno 5, o tres 5, u otra cosa), siempre tendré los $ 10 exactos necesarios.

Como segundo ejemplo, tengo $ 0.16 en mi bolsillo. ¿Qué otras cantidades de dinero debo poder pagar exactamente?

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

¿Qué pasa si tengo $ 0.27 en mi bolsillo?

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

En el caso anterior, solo había unas pocas cantidades de dinero para las cuales siempre tendría un cambio perfecto.

Tu tarea

Escriba el programa más corto (o función con nombre) que toma A) una cantidad entera de dinero y B) una lista de posibles denominaciones como entrada, y genera una lista de las cantidades de dinero para las cuales debo tener un cambio perfecto. La entrada puede ser STDIN o argumentos para el programa o la función. No voy a ser muy estricto con el formato de entrada; puede coincidir con la forma en que su idioma formatea las matrices.

Quizás una explicación más detallada

Tengo una cierta cantidad de dinero en mi bolsillo, que se forma a partir de un conjunto de posibles demostraciones de moneda. Si tengo $ 8, y sé que las posibles denominaciones son $ 2 y $ 3, entonces solo hay muchas combinaciones diferentes de billetes que podrían estar en mi bolsillo. Estos son 2+2+2+2y 3+3+2. Para poder producir una cantidad exacta de dinero, tengo que poder producir esa cantidad usando solo los billetes que tengo en el bolsillo. Si tuviera cuatro 2s, podría producir 2, 4, 6, or 8. Si tuviera dos 3 y un 2, podría producir 2, 3, 5, 6, or 8Ya que no sé cuál de estas combinaciones tengo en mi bolsillo, mi respuesta final se reduce a 2, 6, 8. Estos son los valores que que podría producir de mi bolsillo, dada la cantidad total y las posibles denominaciones.

Ejemplo de E / S calculado 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
PhiNotPi
fuente
Esto no está muy claro.
motoku
@FryAmTheEggman He agregado una "quizás una explicación más detallada". Avísame si aún es confuso. (También eliminé un caso de borde porque era bastante inútil.)
PhiNotPi
No veo cómo te va 6 [2, 3, 4]. ¿No 2+2+2puede hacer 3, y 3+3no hacer 2 y 4?
xnor
@xnor estás en lo correcto, arreglado.
PhiNotPi
¿Debe el programa ejecutarse en un tiempo razonable para todas las entradas? Por ejemplo, mi idea más breve es exponencial en la cantidad inicial y 2 ^ 1500 es mucho.
randomra

Respuestas:

2

Python 2, 200 197 193 140 bytes

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}

(Gracias a @Nabb por los consejos)

Aquí hay una solución mal desarrollada por ahora para comenzar las cosas. Llamar con g(16, [1, 5, 10, 25]): la salida es un conjunto con las denominaciones relevantes.

El enfoque es sencillo y se divide en dos pasos:

  • fanaliza todas las formas de llegar ncon denominaciones D(por ejemplo [1, 5, 10]), y para cada una calcula todas las cantidades que se pueden hacer con estas denominaciones (por ejemplo set([0, 1, 5, 6, 10, 11, 15, 16])).
  • gcalcula las intersecciones de los resultados de f, luego elimina 0 para la respuesta final.

El programa resuelve bien los casos 1-5 y 7, el desbordamiento de la pila en 6 y dura 8 para siempre

Si no hay solución (p g(7, [2, 4, 6]). Ej. ), El programa devuelve un conjunto vacío. Si se permite lanzar un error para tal caso, entonces aquí hay un resumen g:

g=lambda*a:reduce(set.__and__,f(*a))-{0}
Sp3000
fuente
g=lambda L,c=0:L and g(L[1:],c)|g(L,c+L.pop(0))or{c}es un poco más corto
Nabb
Un poco más al pasar -{0}a g y usar en [L]*-~nlugar de[L][-n:]
Nabb
1

JavaScript (ES6) 162 203 207

Editar Cambió la forma de intersectar conjuntos de resultados en la matriz r. Un poco más rápido, pero el algoritmo todavía apesta.

Una explicación más detallada seguirá.
En resumen: c es una función recursiva que enumera todas las subdivisiones posibles. k es una función recursiva que enumera todas las sumas posibles sin repeticiones. Cualquier nuevo conjunto de resultados encontrado con la función k se compara con el conjunto anterior encontrado, solo se mantienen los resultados comunes.

¿Por qué es tan lento? Tener que administrar un objetivo total de, digamos, 1500 y una sola pieza de valor 1, enumerar todas las sumas posibles no es una buena 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

Sin 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
}

Prueba en la consola Firefox / FireBug

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

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

(tiempo 84 ms)

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

[1, 2, 25, 26, 27]

(tiempo 147252 ms, así que no tan rápido)

edc65
fuente
0

Wolfram Methematica, 104 bytes

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

Ungolfed (leído desde el final):

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
fuente