Przy stole jest ludzi. th osoba musi zapłacić dolarów.
Niektórzy ludzie nie mają odpowiednich rachunków, aby zapłacić dokładnie , dlatego opracowali następujący algorytm.
Po pierwsze, wszyscy kładą na stole część swoich pieniędzy. Następnie każda osoba odbiera nadpłacone pieniądze.
Rachunki mają ustalony zestaw nominałów (nie stanowi części danych wejściowych).
Przykład: Załóżmy, że są dwie osoby, Alice i Bob. Alice jest winna 5 USD i ma pięć rachunków za 1 USD . Bob jest winien 2 USD i ma jeden 5 USD . Po tym, jak Alice i Bob położyli wszystkie swoje pieniądze na stole, Bob zabiera 3 $ i wszyscy są szczęśliwi.
Oczywiście są chwile, w których nie trzeba odkładać wszystkich pieniędzy na stół. Na przykład, jeśli Alicja miała tysiąc rachunków za 1 USD , nie musi kłaść ich wszystkich na stole, a następnie odbierać większość z nich.
Chcę znaleźć algorytm o następujących właściwościach:
Dane wejściowe określają liczbę osób, ile każda osoba jest winna i ile rachunków każdej denominacji ma każda osoba.
Algorytm mówi każdej osobie, które rachunki należy postawić na stole w pierwszej rundzie.
Algorytm mówi każdej osobie, które rachunki należy usunąć ze stołu w drugiej rundzie.
Liczba rachunków postawionych na stole + liczba rachunków usuniętych ze stołu jest zminimalizowana.
Jeśli nie ma wykonalnego rozwiązania, algorytm po prostu zwraca błąd.
źródło
Odpowiedzi:
Jeśli ponownie rozwiążesz problem w nieco inny (ale równoważny) sposób, algorytm stanie się bardziej oczywisty:
Oznacza to, że po zakończeniu posiłku restauracja powinna mieć 61 USD , Alice powinna mieć 11 USD , Bob powinien mieć 1 USD, a Carl 5 USD .
Nominały rachunków nie mają znaczenia, ale dla tego przykładu wybrałem nominały papierowej waluty amerykańskiej, ponieważ są one znane.
Kontynuując nasz przykład:
wskazuje, że Alice zaczęło się $ 1 $ 5, $ 10 $ 20 Bob zaczął z $ 1 $ 1 $ 5, $ 5, a Carl rozpoczął z $ 10 i $ . 20
Ponownie, celem jest zminimalizowanie liczby rachunków, które zmieniają ręce. Innymi słowy:
Pierwsze ograniczenie mówi, że rozwiązanie może przypisać konkretny rachunek tylko jednej stronie, a druga gwarantuje, że wszyscy zapłacą odpowiednią kwotę.
Jest to problem z PROGRAMOWANIEM INTEGRALNYM 0,1 i jest kompletny NP (patrz [ Karp 1972 ]). Strona Wikipedii na temat programowania liniowego zawiera informacje na temat różnych algorytmów, które można zastosować w przypadku tego rodzaju problemów.
Istnieje potencjalnie wiele optymalnych rozwiązań; ręcznie pierwszym rozwiązaniem wymyślonego przeze mnie przykładu było:
co oznacza, Alicja płaci dokładnie $ 5 i $ 20 Bob płaci dokładnie $ 1 $ 5 i $ 5, a Carl overpays $ 10 i $ 20, a następnie usuwa $ 5 Z tabeli.
Użyłem również modułu Mixed Integer Linear Program systemu Sage Math , który ma możliwość korzystania z różnych backendów solvera ( GLPK , COIN , CPLEX lub Gurobi ). Pierwszym rozwiązaniem było
co jest prawie takie samo, z wyjątkiem tego, że Carl wziął „inne” 5 $, które Bob postawił na stole.
Zidentyfikować grupę osób, które mogą zapłacić obniżoną sumę? A może część ludzi, którzy wciąż mogą zapłacić cały rachunek, tj. Płacą za przyjaciela.
Ostatnie oświadczenie sprawia, że wydaje się, że interesuje Cię ustalenie nominałów rachunków, ale nie zmienia to problemu.
źródło
Z pewnością niektórymi podstawowymi początkami mogą być najmniejsze dostępne rachunki, aby osiągnąć całkowitą kwotę ogólnego rachunku. Jeśli nie chcesz pozwolić na rachunki w wysokości 2 USD , każda osoba może po prostu usunąć największy rachunek, jaki może wziąć z puli, i dotrze tam stosunkowo szybko. $ 2 Bill że rzuca się jak to nie sub-divide ładnie się do innych wyznań i znacznie komplikuje problem. Z pewnością można również przeprowadzić szereg innych optymalizacji, aby dokonać obserwacji na temat potrzeby dodania dodatkowych środków podczas pierwszej rundy (na przykład, jeśli łączna liczba rachunków o wartości 1 USD jest kiedykolwiek wystarczająca na pokrycie rachunku, wówczas wszyscy mogą przestać wkładać, chyba że nie wpłacili jeszcze wystarczającej kwoty na rachunek).
źródło