¿Cómo le pido dinero a un cajero en el banco?

35

Necesito ir al banco y retirar algo de dinero. Necesito retirar $ 30, $ 22 para pagarle a mi compañero de cuarto por internet y $ 8 para lavar la ropa. Como ninguno de estos puede cambiar, necesito que mis $ 30 se dividan en dos particiones de los dos tamaños. Eso significa que cuando el cajero me pregunte cómo quiero mis $ 30 voy a tener que hacer una solicitud. Podría decirles que lo quiero en veinte, cinco y cinco unidades. Pero quiero hacer mi solicitud lo más simple posible para evitar tener que repetirme. Para simplificar mi solicitud, podría pedir que mi efectivo contenga veinte y al menos 2 unidades porque el 8 está implícito en el total, pero mejor aún, podría simplemente solicitar que una de las facturas que reciba sea un billete de un dólar (si usted No está convencido de esto, solo trate de ganar 29 dólares sin ganar 8).

Así que todo está bien, pero necesito hacer este cálculo cada vez que voy al banco, así que pensé en escribir un programa para hacer esto (¿tienes que escribir un programa para hacer esto por mí?).

Su programa o función debe tomar una lista de enteros que represente todos los pagos que necesito hacer y un conjunto de enteros que representen las denominaciones de billetes disponibles en el banco, y debe generar la lista más pequeña de denominaciones de tal manera que se pueda hacer el total eso incluye que la lista de denominaciones se puede dividir limpiamente en la lista de pagos.

Reglas extra

  • Puede suponer que la lista de denominaciones siempre contendrá una 1o puede agregarla a cada lista usted mismo.

  • Algunas entradas tendrán múltiples soluciones mínimas. En estos casos, puede generar cualquiera de los dos.

Este es el por lo que las respuestas se puntuarán en bytes, siendo mejores menos bytes.

Casos de prueba

Payments, denominations    -> requests
{22,8}    {1,2,5,10,20,50} -> {1} or {2}
{2,1,2}   {1,5}            -> {1}
{20,10}   {1,2,5,10,20,50} -> {}
{1,1,1,1} {1,2}            -> {1,1,1}
{20,6}    {1,4,5}          -> {1}
{2,6}     {1,2,7}          -> {2}
{22, 11}  {1, 3, 30, 50}   -> {1, 3}
{44, 22}  {1, 3, 30, 50}   -> {1, 3, 3, 30}
Asistente de trigo
fuente
22
Al principio pensé que esto era spam o fuera de tema o algo así ...
Erik the Outgolfer
1
Los párrafos de @EriktheOutgolfer duelen mucho los desafíos> _ <
Urna de pulpo mágico
2
Creo que debe incluir al menos un caso de prueba donde la solicitud debe ser algo más que billetes de un dólar, como {2,6} {1,2,7} -> {2}.
Arnauld
@Arnauld He agregado tu caso
Wheat Wizard
1
(If you are not convinced of this just try to make 29 dollars without making 9)¿Quieres decir sin hacer 8? ¿O no entendí mal?
undergroundmonorail

Respuestas:

5

JavaScript (ES6), 485 476 bytes

Muy bien ... esto es un monstruo. :-(
Pero es un monstruo bastante rápido que resuelve todos los casos de prueba casi al instante.

Puede que intente jugar golf más avanzado más tarde, pero ya he dedicado demasiado tiempo a ello.

f=(b,a,L=[...a])=>L.reduce((a,x)=>[...a,...a.map(y=>[x,...y])],[[]]).sort((a,b)=>a[b.length]||-1).find(L=>(Y=G(U(b)-U(L),L.sort((a,b)=>a-b)),Y[0]&&!Y.some(a=>P(b.map(a=>G(a,[]))).every(b=>b+''!=a))),U=a=>~~eval(a.join`+`),P=(e,C=[],R=[])=>e[0].map(v=>R=(c=v.map((x,i)=>x+(C[i]|0)),e[1])?[...P(e.slice(1),c),...R]:[c,...R])&&R,G=(n,l)=>(S=[],g=(n,l)=>n?a.map(x=>x<l[0]|x>n||g(n-x,[x,...l])):S=[l.map(v=>s[a.indexOf(v)]++,s=[...a].fill(0))&&s,...S])(n,l)&&S)||f(b,a,[...a,...L])

Casos de prueba

¿Cómo?

NB: Esto ya no coincide con la versión actual, pero es mucho más fácil de leer de esa manera.

// b = list of payments, a = list of bills,
// L = list from which the requested bills are chosen
f = (b, a, L = [...a]) => (
  // U = helper function that computes the sum of an array
  U = a => ~~eval(a.join`+`),

  // P = function that computes the summed Cartesian products of arrays of integers
  // e.g. P([[[1,2],[3,4]], [[10,20],[30,40]]]) --> [[33,44], [13,24], [31,42], [11,22]]
  P = (e, C = [], R = []) => e[0].map(v => R =
    (c = v.map((x, i) => x + (C[i] | 0)), e[1]) ? [...P(e.slice(1), c), ...R] : [c, ...R]
  ) && R,

  // G = function that takes a target amount and a list of requested bills and returns
  // all combinations that contain the requested bills and add up to this amount;
  // each combination is translated into a list of number of bills such as [2,0,0,1,0]
  G = (n, l) => (
    S = [],
    g = (n, l) => n ?
      a.map(x => x < l[0] | x > n || g(n - x, [x, ...l])) :
      S = [l.map(v => s[a.indexOf(v)]++, s = [...a].fill(0)) && s, ...S]
  )(n, l) && S,

  // compute X = list of possible bill combinations to process all payments
  X = P(b.map(a => G(a, []))),

  // compute the powerset of L and sort it from shortest to longest list
  L.reduce((a, x) => [...a, ...a.map(y => [x, ...y])], [[]])
  .sort((a, b) => a[b.length] || -1)

  .find(L => (
    // compute Y = list of possible combinations to reach the total amount,
    // using the requested bills
    Y = G(U(b) - U(L), L.sort((a, b) => a - b)),

    // exit if Y is not empty and all combinations in Y allow to generate all payments
    Y[0] && !Y.some(a => X.every(b => b + '' != a)))
  )

  // if no solution was found, enlarge the set of requested bills and try again
  || f(b, a, [...a, ...L])
)
Arnauld
fuente
No estoy muy familiarizado con javascript, pero ¿puedes reducir el &&to &y el ||to |?
Taylor Scott
@TaylorScott Esto solo es posible bajo ciertas condiciones. Por ejemplo, a || bevaluará bsolo si aes falso, mientras a | bque incondicionalmente realizará un OR bit a bit entre ay b.
Arnauld
4

Python 2 , 456 455 bytes

¡Extremadamente, extremadamente, extremadamente lento! Debería funcionar correctamente en todos los ejemplos de entrada con tiempo suficiente

Editar: guardado 1 byte gracias a @Jonathan Frech

def F(p,d):v=sum(p);E=enumerate;l=lambda x,y:y[1:]and(x>=y[-1]and[k+[y[-1]]for k in l(x-y[-1],y)]+l(x,y[:-1])or l(x,y[:-1]))or[[1]*x];Q=l(v,d);m=lambda x,y=[0]*len(p):x and max(m(x[1:],[a+x[0]*(i==j)for i,a in E(y)])for j,_ in E(y))or y==p;f=lambda x,h=[]:x and min([S for i,s in E(x)for S in h+[s],f(x[:i]+x[i+1:],h+[s])if all(map(m,filter(lambda k:all(k.count(j)>=S.count(j)for j in S),Q)))],key=len)or[1]*v;print-(all(map(m,Q))-1)*min(map(f,Q),key=len)

Pruébalo en línea!

Explicación

p,d=input() # Read input
v=sum(p) # Save a byte by keeping track of the total money withdrawn
E=enumerate # We use enumerate a lot
# Generates the possible combinations of denominators that add up to the withdrawn amount 
l=lambda x,y:y[1:]and(x>=y[-1]and[k+[y[-1]]for k in l(x-y[-1],y)]+l(x,y[:-1])or l(x,y[:-1]))or[[1]*x]
# We use the list generated by l quite a few times
Q=l(v,d)
# Checks if we can divide a list of denominators x in such a way that we get the wished division of the money
m=lambda x,y=[0]*len(p):x and max(m(x[1:],[a+x[0]*(i==j)for i,a in E(y)])for j,_ in E(y))or y==p
# For a list of denominators, it tries all possible combinations of the denominators as input to the teller, selecting the one with minimum length
f=lambda x,h=[]:x and min([S for i,s in E(x)for S in h+[s],f(x[:i]+x[i+1:],h+[s])if all(map(m,filter(lambda k:all(k.count(j)>=S.count(j)for j in S),Q)))],key=len)or[1]*v
# Call f with all possible lists of denominators, and check if saying nothing to the teller will work
print-(all(map(m,Q))-1)*min(map(f,Q),key=len)
Halvard Hummel
fuente
1
Usar una función en lugar de input()es un byte más corto .
Jonathan Frech