Dividindo a conta

12

Tarefa

Suponha que o ppepole tenha que dividir uma conta; cada um deles é identificado por um triplo (Name, n, k)composto por:

  • Name: o nome ;
  • n: o valor que ela / ele deve pagar ;
  • k: a quantia que ele realmente pagou .

O desafio aqui é descobrir quanto quem deve a quem.

Suposições

  • A entrada e a saída podem estar em qualquer formato conveniente.

  • p N,n N+, k N.

  • p >1.

  • Os nomes são cadeias únicas de comprimento arbitrário, compostas por caracteres do alfabeto em minúsculas.

Solução

A solução é representada pelo conjunto mínimo de transações entre as ppessoas; em particular, eles são triplos(from, to, amount)

  • from: nameda pessoa que dá dinheiro;
  • to: nameda pessoa que recebe dinheiro;
  • amount: quantidade de dinheiro da transação.

NOTA : A soma de todas as dívidas ( n) pode diferir da soma de todos os valores já pagos ( k). Nesse caso, você deve adicionar a saída ('owner', Name, amount)ou (Name, 'owner', amount)o formato escolhido. Qualquer nome nunca será owner. A string 'owner' é flexível.

Se existirem vários conjuntos mínimos, selecione aquele com a soma mínima de todos os valores da transação (valores absolutos); em caso de empate, escolha um deles.

Casos de teste:

inputs(Name,n,k):
[('a',30,40),('b',40,50),('c',30,15)]
[('a',30,30),('b',20,20)]
[('a',30,100),('b',30,2),('c',40,0)]
[('a',344,333),('b',344,200),('c',2,2)]
[('a',450,400),('b',300,300),('c',35,55)]

outputs(from, to, amount):
[('c','a',10),('c','b',5),('owner','b',5)] or [('c','b',10),('c','a',5),('owner','a',5)]
[]
[('owner','a',2),('b','a',28),('c','a',40)] PS: [('owner','a',2),('b','a',68),('c','b',40)] has the same number of transactions, but it is not a valid answer, because the total amount of its transaction is greater than that of the proposed solution.
[('a','owner',11),('b','owner',144)]
[('a','owner',30),('a','c',20)]

Este é o código-golfe: o código mais curto vence .

Vedant Kandoi
fonte
1
Eu acho que você deveria alterar "Você precisa gerar o cenário mais simplificado que requer menor número de transações". para "Você deve exibir o cenário que requer o menor número de transações. Se existirem vários cenários, escolha um com o menor número total de transações". pois acredito que é mais claro.
Jonathan Allan
1
Bom desafio. Mas provavelmente será necessário casos de teste mais complexos para garantir que as soluções sejam sempre ideais.
Arnauld
3
O que é "menos total nocional"?
lirtosiast
1
@ JonathanAllan ainda está confuso com a palavra "nocional". De onde aquilo está vindo? com base no caso de teste 3, parece que você deve preferir respostas em que a mesma pessoa não dá nem recebe? isso está correto? Também por que isso é descrito como nocional?
Jonah
1
@Jonah eu usei "total nocional", já que as direções das transações não devem ser consideradas (apenas seu tamanho absoluto), embora agora eu perceba que é um pouco redundante (pois ter duas transações que se contrapõem não seria considerada uma vez que o desempate !). [Nocional é apenas o termo usado em finanças.]
Jonathan Allan

Respostas:

2

JavaScript (ES6),  252 227 223 222  215 bytes

Toma entrada como [[n0, k0, name0], [n1, k1, name1], ...].

As transações na solução podem ser positivas ou negativas. O proprietário é chamado indefinido .

a=>(m=g=(B,t,s)=>B.some(x=>x)?B.map((b,x)=>B.map((c,y)=>b*c<0&b*b<=c*c&&g(C=[...B],[...t,[a[x][2],b,a[y][2]]],s+a.length-1/b/b,C[C[y]+=b,x]=0))):m<s||(r=t,m=s))([...a.map(([x,y])=>t-(t+=y-x),t=0),t],[],a.push(g))&&r

Experimente online!

Comentado

a => (                              // a[] = input array
  m =                               // initialize m to a non-numeric value
  g = (B, t, s) =>                  // g = function taking: B = balances, t = transactions,
                                    //     s = score of the current solution
    B.some(x => x) ?                // if at least one balance is not equal to 0:
      B.map((b, x) =>               //   for each balance b at position x:
        B.map((c, y) =>             //     for each balance c at position y:
          b * c < 0 &               //       if b and c are of opposite sign
          b * b <= c * c &&         //       and |b| <= |c|,
          g(                        //       do a recursive call to g:
            C = [...B],             //         - with a copy C of B
            [ ...t,                 //         - append the new transaction to t[]
              [a[x][2], b, a[y][2]] //           in [from_name, amount, to_name] format
            ],                      //
            s + a.length - 1/b/b,   //         - add (a.length - 1/b²) to s
            C[C[y] += b, x] = 0     //         - update C[y] and clear C[x]
          )                         //       end of recursive call
        )                           //     end of inner map()
      )                             //   end of outer map()
    :                               // else:
      m < s ||                      //   if the score of this solution is lower than m,
      (r = t, m = s)                //   update r to t and m to s
)(                                  // initial call to g:
  [                                 //   build the list of balances:
    ...a.map(([x, y]) =>            //     each balance is equal to:
      t - (t += y - x),             //     due_value - paid_value
      t = 0                         //     keep track of the total t ...
    ),                              //
    t                               //   ... which is appended at the end of this array
  ],                                //   (this is the balance of the owner)
  [],                               //   start with t = []
  a.push(g)                         //   append a dummy owner to a[]; start with s = 1
) && r                              // return r
Arnauld
fonte