Processing math: 4%

wtorek, 6 sierpnia 2013

8736. Arka Noego [NOE]

Zadanie:
https://pl.spoj.com/problems/NOE

Skrócony opis zadania:
Mając n liczb z przedziału \left<1;10^9\right> znajdź liczbę, która nie ma pary (wszystkie pozostałe mają).
Np. dla liczb: 1 2 3 2 1 wynik to 3, gdyż 1 i 2 mają pary.



Rozwiązanie naiwne:
Możemy dla każdego elementu sprawdzać wszystkie pozostałe i jeśli nie znajdziemy żadnego do pary to robić breaka i go wypisać.

Złożoność: O(n^2), bo dla każdego z n liczb sprawdzamy n-1 pozostałych.

Rozwiązanie sprytniejsze:
Możemy mapować te elementy. Czyli innymi słowy mamy sobie tablicę haszującą tab, która na początku jest pusta. Następnie dla każdej liczby x wykonujemy ++tab[x]. Po przejściu wszystkich elementów sprawdzamy, która komórka ma wartość 1 i wypisujemy jej klucz.

Złożoność: O(n \log n), jeśli użyjemy mapy z STLa. Przy użyciu dobrej tablicy haszującej, która dodaje elementy w czasie stałym nadal będzie to O(n \log n), gdyż będziemy musieli przejść całą tę tablicę.

Rozwiązanie sprytniejsze nr 2:
Możemy posortować liczby z wejścia. Następnie przechodzimy je od początku (lub końca - obojętnie) i sprawdzamy kolejne pary liczb. Jeśli pierwsza liczba z pary nie jest równa drugiej, to wypisujemy tę pierwszą. Jeśli przejdziemy całą tablicę i nie napotkamy takiej "nierównej" pary, to wypisujemy ostatnią liczbę.
Np. dla powyższego przykładu: 1 2 3 2 1 po posortowaniu liczby będą wyglądać tak: 1 1 2 2 3. Jak widać po przejściu całej tablicy okaże się, że wszystkie pary się zgadzają, więc wypisujemy jedyną liczbę bez pary, czyli tę ostatnią.
Wadą tego rozwiązania jest zależność od n. Jeśli n jest duże, to nie będziemy mogli stablicować wszystkich liczb, gdyż nie starczy nam pamięci. Najlepsze rozwiązanie jest niezależne pamięciowo od n.

Złożoność: O(n \log n), ze względu na sortowanie.

Rozwiązanie najsprytniejsze:
Możemy też coś zaobserwować. Mamy zawsze pary liczb i tylko jedną nie do pary. Moglibyśmy zatem użyć jednej z tych liczb do anulowania drugiej. Tzn. jeżeli pojawi się pierwsza liczba, to jest coś ustawiane, a kiedy pojawi się druga, to jest to resetowane. Taką właśnie operacją jest XOR (symbol \oplus). Jeśli ktoś nie wie, x \oplus x = 0 (bo para bitów na każdej pozycji jest taka sama, co zwraca dla każdej pozycji 0). Ponadto 0 \oplus x = x. Tak więc możemy po prostu ustawić zmienną x na 0, a potem dla każdej z n liczb wykonywać x \oplus= a_i (gdzie a_i to kolejna liczba z wejścia). Na końcu wszystkie pary się anulują i zostanie jedna liczba będąca tą bez pary.

Złożoność: O(n), bo dla każdej liczby wykonujemy tylko jedną operację. Kolejną zaletą tego rozwiązania jest niezależność od pamięci - złożoność pamięciowa to O(1).

Ostateczny algorytm:
int main()
{
    int x = 0, y;
    while(get(y))
        x ^= y; // ^ to operator XOR w C/C++
    print(x);
}

Brak komentarzy:

Prześlij komentarz