Якщо ви перестановите проблему дещо іншим (але еквівалентним) способом, алгоритм стає більш очевидним:
Є залучених сторін: люди, і один restauarant. Нехай - це сума грошей, яку повинна мати після закінчення та оплати їжі. Наприклад, якщо Аліса $ 36 і заборгував $ 25, Боб має $ 12 і повинен $ 11, і Карл має $ 30 і заборгував $ 25, ми говоримо , що є ресторан і є:nn−1piip0
p=(61,11,1,5)
Тобто, коли їжа закінчиться, ресторан повинен мати 61 долар , Аліса - 11 доларів , у Боба - 1 долар, а в Карла - 5 доларів .
Тепер дозвольмо перерахувати всі рахунки. Наприклад:bm
b=(1,5,10,20,1,1,5,5,10,20)
Номінали векселів не мають значення, але я вибрав номінали паперової американської валюти для цього прикладу, оскільки вони знайомі.
Ми прагнемо мінімізувати кількість рахунків, які змінюють руки, тому ми пов'язуємо "вартість" з особою, яку залишаю, з рахунком , використовуючи матрицю . Записи 0 у цій матриці вказують, з яких рахунків починається кожна сторона ( для всіх оскільки ресторан починається без рахунків).ij{0,1}CC0,j=0j
Продовжуючи наш приклад:
C=⎡⎣⎢⎢⎢0011001100110011010101010101010101100110⎤⎦⎥⎥⎥
вказує на те, що Аліса починала з $ 1, $ 5, $ 10, $ 20, Боб починав з $ 1, $ 1, $ 5, $ 5, а Карл починав з $ 10 і $ 20.
Знову ж таки, мета - мінімізувати кількість рахунків, які змінюють руки. Іншими словами:
Minimize:subject to:and∑i=0n−1∑j=0m−1Ci,jxi,j∑i=0n−1xi,j=1 for 0≤j<m,∑j=0m−1xi,jbj=pi for 0≤i<n,xi,j≥0
Перше обмеження говорить про те, що рішення може призначити певний вексель лише одній стороні, а друге забезпечує, щоб усі сплатили відповідну суму.
Це проблема 0,1 ІНТЕГЕРНОГО ПРОГРАММУВАННЯ і є NP-завершеною (див. [ Karp 1972 ]). На сторінці Вікіпедії про лінійне програмування є інформація про різні алгоритми, які можна використовувати для таких типів проблем.
Є потенційно безліч оптимальних рішень; від руки першим рішенням прикладу, який я придумав, було:
x=⎡⎣⎢⎢⎢0100100001001000101000000001100010001000⎤⎦⎥⎥⎥
що означає Аліса платить рівно $ 5 і $ 20, Боб платить рівно $ 1, $ 5 і $ 5, і Карл переплачує $ 10 і $ 20 , а потім видаляє $ 5 з таблиці.
Я також використав змішаний модуль Integer лінійного програмування в Sage Math системи , яка має можливість використовувати різні движки решателя ( GLPK , МОНЕТІ , CPLEX або Gurobi ). Перше рішення, яке воно дало, було
x=⎡⎣⎢⎢⎢0100100001001000001010000000100110001000⎤⎦⎥⎥⎥
що майже те саме, за винятком того, що Карл взяв "інші" 5 доларів, які Боб поклав на стіл.
Формулювання задачі таким чином задовольняє всі перелічені вами властивості (ви можете екстраполювати, які рахунки закінчуються з і матриця рішення ). Виняток - можливо, №4, про який йшлося в коментарях до питання. Мені незрозуміло, що ви хотіли б зробити в ситуації, коли не існує можливого рішення набору лінійних рівнянь:xCx
Визначте підмножину людей, які можуть сплатити зменшену суму? Або, можливо, підмножина людей, які все-таки могли оплатити весь рахунок, тобто вони платять за свого друга.
У вашій остаточній заяві здається, що ви зацікавлені у тому випадку, коли номінали векселів виправлені, проте це не змінить проблеми.
У будь-якому випадку, є також рішення в якому кожна людина оплачує кредитну карту.O(1)