Расщепление счета

12

задача

Предположим, что pлюди должны разделить счет; каждый из них идентифицирован тройкой, (Name, n, k)состоящей из:

  • Name: имя ;
  • n: сумму, которую она / он должен заплатить ;
  • k: сумма, которую он / она фактически заплатил .

Задача здесь состоит в том, чтобы выяснить, сколько кому кто должен.

Предположения

  • Ввод и вывод могут быть в любом удобном формате.

  • p N,n N+, k N.

  • p >1.

  • Имена - это уникальные строки произвольной длины, состоящие из строчных букв алфавита.

Решение

Решение представлено минимальным набором транзакций среди pлюдей; в частности они тройки(from, to, amount)

  • from: nameЧеловек , который дает деньги;
  • to: nameЧеловек , который получает деньги;
  • amount: сумма денег по сделке.

ПРИМЕЧАНИЕ . Сумма всех долгов ( n) может отличаться от суммы всех уже оплаченных сумм ( k). В этом случае вы должны добавить в вывод ('owner', Name, amount)или (Name, 'owner', amount)в формате, который вы выбрали. Любое имя никогда не будет owner. Строка 'owner' является гибкой.

Если существует несколько наборов минимальных значений, выберите набор с минимальной суммой всех сумм транзакций (абсолютные значения); в случае галстука, выберите один из них.

Тестовые случаи:

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

Это код-гольф: самый короткий код выигрывает .

Ведант Кандой
источник
1
Я думаю, что вы должны изменить «Вы должны вывести наиболее упрощенный сценарий, который требует наименьшего количества транзакций». на «Вы должны вывести сценарий, который требует наименьшего количества транзакций. Если существует несколько таких сценариев, выберите один с наименьшим общим количеством транзакций». так как я считаю, это понятнее.
Джонатан Аллан
1
Хороший вызов. Но, вероятно, потребуются более сложные контрольные примеры, чтобы убедиться, что решения действительно всегда оптимальны.
Арно
3
Что такое «наименее общее понятие»?
lirtosiast
1
@JonathanAllan все еще смущен словом «условный». откуда это? на основании теста 3 похоже, что следует отдавать предпочтение ответам, когда один и тот же человек не дает и не получает? это верно? и почему это описывается как условное?
Иона
1
@Jonah Я использовал «общее значение», так как направления транзакций не должны учитываться (только их абсолютный размер), хотя теперь я понимаю, что это несколько избыточно (поскольку две транзакции, которые противодействуют друг другу, не будут считаться разрывами связей) !). [Условный - это просто термин, используемый в финансах.]
Джонатан Аллан

Ответы:

2

JavaScript (ES6),  252 227 223 222  215 байт

Принимает вход как [[n0, k0, name0], [n1, k1, name1], ...].

Транзакции в решении могут быть как положительными, так и отрицательными. Владелец называется неопределенным .

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

Попробуйте онлайн!

комментарии

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
источник