Processing math: 2%

środa, 28 sierpnia 2013

8994. Taksówka na Manhattanie [TAXIMAN]

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

Skrócony opis problemu:
Mając dane n punktów, znajdź odległość między dwoma najbardziej oddalonymi od siebie punktami (w metryce Manhattan - np. odległość między (1;1) a (2;2) to 2, bo trzeba iść 1 w górę i 1 w prawo).



Rozwiązanie naiwne:
Dla każdej pary punktów sprawdzamy odległość między nimi ze wzoru: d(A,B) = |x_A-x_B|+|y_A-y_B| i wybieramy największą z nich.

Złożoność: O(n^2), bo sprawdzamy każdą parę punktów, a jest ich \frac{n(n-1)}{2}.

Rozwiązanie sprytne:
Znajdujemy najmniejszy prostokąt, który zawiera wszystkie punkty. Ogranicza się to do znalezienia najmniejszych oraz największych współrzędnych x i y (będą to 4 wartości). Następnie znajdujemy w czasie liniowym 4 punkty, które są najbliższe rogom tego prostokąta. Na koniec, mając te 4 punkty, wypisujemy większą z dwóch odległości: od lewego dolnego do prawego górnego oraz od lewego górnego do prawego dolnego.

Złożoność: O(n), bo prostokąt znajdujemy w czasie liniowym, jak i najbliższe rogom punkty.

Rozwiązanie sprytne nr 2:
Określmy odległość każdego punktu od (0;0). Jest to |y_i|+|x_i|. Mając 2 punkty możemy obliczyć odległość między nimi za pomocą ich odległości od (0;0). Np. dla (1;2) i (6;7) wartości to odpowiednio 3 i 13. Odległość między nimi to zatem |3-13|=10. ALE dla punktów (1;7), (6;-1) to nie działa. Dlaczego? Bo dla pierwszej pary odcinek wygląda jak / a dla drugiej jak \ . W pierwszym przypadku jest dobrze, bo odległość między pierwszym punktem i (0;0) ma wspólny początek z odległością między drugim punktem i (0;0), a w drugim przypadku niekoniecznie. Jak możemy to naprawić? Obracając układ współrzędnych albo, konkretniej, oś OX, zamieniając znaki wszystkim x. Dla punktów (1;7), (6;-1) obliczamy zatem dwie wartości: y_i+x_i (8 dla pierwszego punktu i 5 dla drugiego) oraz y_i-x_i (6 dla pierwszego punktu i -7 dla drugiego). Z ich pomocą możemy obliczyć ich odległość - jest to większa z |8-5|=3 i |6+7|=13, a więc 13. Możemy więc znaleźć min i max dla y_i+x_i oraz min2 i max2 dla y_i-x_i w czasie liniowym. Co z tym robimy? Porównujemy |max-min| oraz |max2-min2| i wybieramy większe z nich. Jest to nasz wynik.

Złożoność: O(n), bo znajdujemy min, max, min2, max2 w czasie liniowym.

Algorytm:
int main()
{
    int n, x, y;
    long long max = -2e9, max2 = -2e9, min = 2e9, min2 = 2e9;
    
    get(n);
    while(n--)
    {
        get(x); get(y);
        if(y+x > max)
            max = y+x;
        if(y+x < min)
            min = y+x;
        if(y-x > max2)
            max2 = y-x;
        if(y-x < min2)
            min2 = y-x;
    }
    print(max2(abs(max-min), abs(max2-min2)));
}

Rozwiązanie sprytne nr 3:
Autorem tego rozwiązania jest paladin, a wzory zostały podane przez Zordona.
Można posłużyć się metryką maksimum. Jest to taka metryka, w której odległość wyraża się następującym wzorem:
d(A,B) = \max\left(|x_A-x_B|, |y_A-y_B|\right)
Aby otrzymać najbardziej odległe punkty wystarczy zatem znaleźć 4 skrajne punkty (po lewej, po prawej, u góry i na dole) a następnie wypisać większą z dwóch odległości między nimi (lewy-prawy, góra-dół).
Jak jednak zamienić metrykę Manhattan na metrykę maksimum? Ano prostym wzorem:
(x,y) \mapsto (x-y, x+y)
Potrzebne uzasadnienie dlaczego to działa. Nie krępujcie się pisać w komentarzach.

Złożoność: O(n), bo dla każdego punktu zamieniamy go na metrykę maksimum w czasie stałym, a potem w czasie liniowym znajdujemy skrajne punkty.

Algorytm:
int main()
{
    int n, x, y;
    long long maxx = -2e9, maxy = -2e9, minx = 2e9, miny = 2e9;
    
    get(n);
    while(n--)
    {
        get(x, y);
        x -= y; y += x+y;
        if(y > maxy)
            maxy = y;
        if(y < miny)
            miny = y;
        if(x > maxx)
            maxx = x;
        if(x < minx)
            minx = x;
    }
    print(max2(abs(maxx-minx), abs(maxy-miny)));
}

Jak widać, rozwiązanie to jest tożsame do poprzedniego (kod jest taki sam praktycznie).

5 komentarzy:

  1. Jest jeszcze jedne rozwiązanie, podobne do algorytmu na średnice grafu(drzewa).
    Znajduję sobie punkt najodleglejszy od np punktu nr 1, wiem że ten punkt który znalazłem jest w optymalnym wyniku i znajduje sobie największą odległość z tego punktu. To jest wynik.

    Oczywiście nie umiem tego udowodnić :)

    OdpowiedzUsuń
  2. Dzięki za info! :-) Szkoda, że nie działa dla większej ilości wymiarów.
    Jeśli spudłowałem z nazwiskiem, to pisz.

    OdpowiedzUsuń
  3. problem w tym, że nie działa również dla 2 wymiarów :)
    co prawda testy z zadania przechodzi, ale można podać prosty przykład:

    4
    4 4
    0 0
    9 0
    8 2

    gdy zaczynamy od pierwszego punktu to dostajemy w wyniku 9,
    w przypadku rozpoczęcia od innego punktu dostajemy 10

    OdpowiedzUsuń
  4. No dla tego przykładu to działa chociaż zmodyfikowana wersja tego algorytmu: bierzemy (4;4), najdalsze jest (9;0), a od niego (4;4) albo (0;0). Musimy więc sprawdzić oba przypadki i dla (0;0) wyjdzie nam 10.
    Ale pewnie są też przykłady, dla których i to nie działa, więc usunąłem.

    OdpowiedzUsuń
  5. Dla dwóch nie, ale dla jednego wymiaru działa ;-)

    OdpowiedzUsuń