Waga
W tym miesiącu omówimy zadanie Waga z finału Potyczek Algorytmicznych 2016.
Opowiada ono o dziwnej wadze Bajtazara, która wyświetla rezultat ważenia zaokrąglony do najbliższej wielokrotności gramów, lub błąd, gdy masa ważonych przedmiotów wynosi co najmniej
gramów. Oprócz wagi Bajtazar posiada jeszcze
innych przedmiotów. Nie zna ich mas, więc chce je ważyć, używając swojej wagi. Nie określi on oczywiście dokładnych mas swoich przedmiotów. Jednak - być może - ważąc ich podzbiory, dla niektórych par przedmiotów będzie mógł stwierdzić, który z nich jest cięższy. I właśnie o to jesteśmy proszeni: mamy wyznaczyć liczbę takich par, mając dane dokładne masy przedmiotów.

Przeanalizujmy, w jaki sposób Bajtazar może porównywać wagi dwóch przedmiotów. Oznaczmy ich masy przez oraz
Weźmy pewien podzbiór pozostałych przedmiotów o masie
Jeśli wskazanie wagi będzie inne w przypadku ładunku o masie
niż dla ładunku o masie
to z całą pewnością można odróżnić wybrane przedmioty.
Załóżmy teraz, że naszych dwóch przedmiotów nie da się porównać w powyższy sposób. Czyli że dla każdego podzbioru pozostałych przedmiotów o masie wskazania wagi dla mas
oraz
będą takie same. Zauważmy ponadto, że takie samo wskazanie wagi otrzymamy dla ładunku o masie
Jeśli zmienimy masy obu rozpatrywanych przedmiotów na
to z punktu widzenia Bajtazara nic się nie zmieni! Dla każdego podzbioru przedmiotów Bajtazar otrzyma przecież dokładnie to samo wskazanie wagi zarówno przed i po takiej zmianie. Dowodzi to, że nie może on w żaden sposób rozróżnić mas tych dwóch przedmiotów, gdyż - z jego punktu widzenia - istnieje możliwość, w której ważą one dokładnie tyle samo, czyli
Wiemy już, w jaki sposób Bajtazar może porównywać masy przedmiotów. Do skonstruowania naszego pierwszego algorytmu rozwiązującego zadanie brakuje nam już tylko jednego prostego spostrzeżenia: jeśli wskazania wagi są różne dla ładunków o masach i
to będą różne również dla ładunków
oraz
Aby sprawdzić, czy Bajtazar może porównać konkretne dwa przedmioty, wystarczy teraz wyznaczyć dla każdego z przedziału
najmniejszą masę podzbioru pozostałych przedmiotów postaci
a potem sprawdzić, czy któraś z nich jest szukanym
rozróżniającym masy
i
Można zrobić to za pomocą programowania dynamicznego. Przy jego obliczaniu zaktualizowanie informacji o nowy przedmiot zajmuje czas
Sprawdzenie jednej pary przedmiotów zadziała w czasie
a całe zadanie potrafimy rozwiązać w czasie
Złożoność ta nie jest jednak satysfakcjonująca.
Toteż musimy analizować nasz problem dalej. Ustalmy trzy takie przedmioty, że pierwszy jest nieporównywalny zarówno z drugim, jak i z trzecim. Oznaczmy ich masy odpowiednio oraz
Możemy teraz zmienić masy pierwszych dwóch przedmiotów na
ponieważ są one nieporównywalne i z punktu widzenia Bajtazara nic to nie zmienia. Pierwszy i trzeci przedmiot nadal są nieporównywalne i oczywiście każda inna para przedmiotów o masach
oraz
również. Ostatecznie Bajtazar nie może rozróżnić masy drugiego i trzeciego przedmiotu. Taka własność relacji bycia nieporównywalnym jest nazywana w matematyce przechodniością. Wraz z innymi w naszym przypadku oczywistymi własnościami zwrotności i symetryczności oznacza to, że mamy do czynienia z relacją równoważności.
Abstrahując jednak od matematycznej nomenklatury, oznacza to po prostu, że przedmioty da się podzielić na rozłączne zbiory (nazywane klasami abstrakcji), takie że dwa przedmioty są nieporównywalne wtedy i tylko wtedy, gdy należą do tego samego zbioru.
Udowodnijmy ponadto, że przedmioty należące do tej samej klasy abstrakcji tworzą spójny przedział wag. Weźmy trzy przedmioty o masach oraz
takie że
oraz przedmioty o masach
i
są nieporównywalne. Wystarczy pokazać, że przedmiot o masie
jest nieporównywalny z przedmiotem o masie
Nie zmieniając instancji problemu z punktu widzenia Bajtazara, możemy zmienić masę pierwszego przedmiotu na
a trzeciego na
Zachęcamy czytelnika do sprawdzenia, iż wskazanie wagi dla dowolnego podzbioru przedmiotów pozostanie po tej zmianie takie samo.
Możemy teraz trochę poprawić nasz algorytm. Najpierw posortujmy przedmioty ze względu na ich masy, a następnie dla każdej pary kolejnych stwierdźmy, czy są one porównywalne. Wystarcza to do wyznaczenia rozmiarów klas abstrakcji, a w szczególności również szukanej liczby par. Otrzymujemy w ten sposób algorytm działający w czasie
Jednak taka złożoność nadal nie wystarcza. Można ją poprawić, używając techniki nazywanej "Plecak bez jednego". Dla każdej pary kolejnych przedmiotów chcemy otrzymać tablicę programowania dynamicznego z dodanymi wszystkimi pozostałymi przedmiotami. Zdefiniujmy rekurencyjną procedurę obliczającą wyniki dla par przedmiotów o indeksach z przedziału
mając daną tablicę programowania dynamicznego
dla przedmiotów spoza danego przedziału. Niech
Najpierw zaktualizujmy
o przedmioty z przedziału
i wykonajmy rekurencyjnie
dla przedziału
Następnie pierwotne
zaktualizujmy o przedmioty z przedziału
i wykonajmy rekurencyjnie
dla przedziału
Obliczmy również wynik dla pary przedmiotów o indeksach
oraz
osobno aktualizując
o wszystkie pozostałe przedmioty. Czas działania
opisuje następujące równanie rekurencyjne
Rozwiązując je, dochodzimy do wniosku, iż złożoność czasowa naszego ostatecznego algorytmu wynosi