Podział rachunku

12

Zadanie

Załóżmy, że ppepole musi podzielić rachunek; każdy z nich jest identyfikowany przez potrójny (Name, n, k)składający się z:

  • Name: nazwa ;
  • n: kwota, którą on / on musi zapłacić ;
  • k: kwota, którą faktycznie zapłacił .

Wyzwanie polega na tym, aby dowiedzieć się, kto jest komu winien.

Założenia

  • Dane wejściowe i wyjściowe mogą być w dowolnym dogodnym formacie.

  • p N,n N+, k N.

  • p >1.

  • Nazwy są unikatowymi ciągami o dowolnej długości, złożonymi z małych liter alfabetu.

Rozwiązanie

Rozwiązanie reprezentuje minimalny zestaw transakcji między pludźmi; w szczególności są trzykrotnie(from, to, amount)

  • from: nameosoby, która daje pieniądze;
  • to: nameosoby, która otrzymuje pieniądze;
  • amount: kwota pieniędzy z transakcji.

UWAGA : Suma wszystkich długów ( n) może różnić się od sumy wszystkich już spłaconych kwot ( k). W takim przypadku musisz dodać wynik ('owner', Name, amount)lub (Name, 'owner', amount)format, który wybrałeś. Żadna nazwa nigdy nie będzie owner. Ciąg „właściciel” jest elastyczny.

Jeśli istnieje kilka zestawów minimalnych, wybierz ten z minimalną sumą wszystkich kwot transakcji (wartości bezwzględne); w przypadku remisu wybierz jeden z nich.

Przypadki testowe:

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)]

To jest golf-golf: wygrywa najkrótszy kod .

Wedant Kandoi
źródło
1
Myślę, że powinieneś zmienić „Musisz przedstawić najbardziej uproszczony scenariusz, który wymaga najmniejszej liczby transakcji”. na „Musisz wygenerować scenariusz, który wymaga najmniejszej liczby transakcji. Jeśli istnieje wiele takich scenariuszy, wybierz jedną z najmniej całkowitą liczbą transakcji”. ponieważ uważam, że jest to jaśniejsze.
Jonathan Allan,
1
Niezłe wyzwanie. Ale prawdopodobnie będzie to wymagało bardziej złożonych przypadków testowych, aby upewnić się, że rozwiązania są zawsze optymalne.
Arnauld,
3
Co to jest „najmniej całkowita hipoteza”?
lirtosiast
1
@JonathanAllan wciąż jest zdezorientowany słowem „hipotetyczny”. skąd to pochodzi? na podstawie przypadku testowego 3 wygląda na to, że należy preferować odpowiedzi tam, gdzie ta sama osoba nie daje i nie odbiera? czy to jest poprawne? także dlaczego jest to określane mianem hipotetycznego?
Jonasz
1
@Jonah użyłem „całkowitej wartości nominalnej”, ponieważ kierunki transakcji nie powinny być brane pod uwagę (tylko ich wielkość bezwzględna), chociaż teraz zdaję sobie sprawę, że jest nieco zbędne (ponieważ posiadanie dwóch transakcji, które przeciwdziałają sobie nawzajem, nie będzie brane pod uwagę po zerwaniu remisu !). [Pojęcie jest tylko terminem używanym w finansach.]
Jonathan Allan,

Odpowiedzi:

2

JavaScript (ES6),  252 227 223 222  215 bajtów

Pobiera dane wejściowe jako [[n0, k0, name0], [n1, k1, name1], ...].

Transakcje w roztworze mogą być dodatnie lub ujemne. Właściciel nazywa się niezdefiniowany .

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

Wypróbuj online!

Skomentował

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
źródło