• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Zadanie Pasy Ruchu ILOCAMP

Mały hosting, OGROMNE możliwości
0 głosów
638 wizyt
pytanie zadane 5 lutego 2023 w C i C++ przez polandonion Dyskutant (7,700 p.)
edycja 18 lutego 2023 przez polandonion

Zadanie Pasy ruchu. Na początku miałem 30%, ale zauważyłem, że niepotrzebnie Y * (X * Y) razy zeruję wszystkie wartości tablicy VIS[]. Po poprawce nadal nie mam 100%, gdyż z 30% zrobiło się ich 90. Jeden test nadal się wywala ze względu na czas.

przed optymalizacją: https://ideone.com/FPi6QR

po: https://ideone.com/gkTgf3

Pomoże ktoś?

2 odpowiedzi

+1 głos
odpowiedź 6 lutego 2023 przez pasjonat_algorytmiki Pasjonat (19,540 p.)
wybrane 6 lutego 2023 przez polandonion
 
Najlepsza

Jeszcze dopowiedziałbym, kilka rzeczy o tym zadaniu.

Oczywiście to co zrobiliście było najłatwiejsze i wzorcowe, ale można podejśc do tego innymi sposobami.

Po 1 traktujesz całośc jako graf, gdzie wierzchołki w polach i,j. Masz ich dokładnie N*M. Puszczasz Dijkstrę równolegle(wrzucasz wszystkie wierzcholki z 1 kolumny, które mają wartosć 0 na seta, i robisz Dijkstrę, gdzie krawędzie masz 3 opcje:

1 - krawędź i, j+1 ma wagę 0.

2 - krawędź i-1, j ma wagę 1

3 - krawędź i+1, j ma wagę 1

I idziesz sobie normalnie Dijkstrą i zauważ, że nie masz krawędzi i, j-1, bo nie możesz się cofać! Kończysz, gdy dojdziesz na pole w ostatniej kolumnie. Takie rozwiązanie ma O(N*M * lg(N*M)), czy coś koło tego, i przechodzi na setkę, napisałem coś takiego, gdybym źle opisał.

#include <iostream>
#include <vector>
#include <set>

using namespace std;

struct Element_seta
{
    int wartosc;
    int y;
    int x;
    bool operator < (const Element_seta &element_seta) const
    {
        if (wartosc == element_seta.wartosc)
        {
            if (y == element_seta.y)
                return x < element_seta.x;
            return y < element_seta.y;
        }
        return wartosc < element_seta.wartosc;
    }
};

int n = 0, m = 0, wczytana_liczba = 0;
vector<vector<int>> plansza;
vector<vector<int>> dp;
set<Element_seta> S;

int main()
{
    // O(N*M * lg(N*M))
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    for (int i = 0; i < n; ++i)
    {
        plansza.push_back({});
        dp.push_back({});
        for (int j = 0; j < m; ++j)
        {
            cin >> wczytana_liczba;
            plansza[i].push_back(wczytana_liczba);
            dp[i].push_back(-1);
        }
    }

    for (int i = 0; i < n; ++i)
    {
        // Dodajemy wierzcholki startowe na seta.
        if (plansza[i][0] == 0)
        {
            S.insert({0,i,0});
            dp[i][0] = 0;
        }
    }

    while(!S.empty())
    {
        Element_seta spr = *S.begin();
        if (spr.x == m-1)
        {
            cout << spr.wartosc << '\n';
            return 0;
        }
        S.erase(spr);
        // Krawedzie o wadze 0.
        if (spr.x + 1 < m)
        {
            if (plansza[spr.y][spr.x+1] == 0)
            {
                if (dp[spr.y][spr.x+1] == -1 || spr.wartosc < dp[spr.y][spr.x+1])
                {
                    auto it = S.find({dp[spr.y][spr.x+1],spr.y,spr.x+1});
                    if (it != S.end())
                        S.erase(*it);
                    dp[spr.y][spr.x+1] = spr.wartosc;
                    S.insert({spr.wartosc,spr.y,spr.x+1});
                }
            }
        }
        // Krawedzie o wadze 1
        if (spr.y + 1 < n)
        {
            if (plansza[spr.y+1][spr.x] == 0)
            {
                if (dp[spr.y+1][spr.x] == -1 || spr.wartosc + 1 < dp[spr.y+1][spr.x])
                {
                    auto it = S.find({dp[spr.y+1][spr.x],spr.y+1,spr.x});
                    if (it != S.end())
                        S.erase(*it);
                    dp[spr.y+1][spr.x] = spr.wartosc + 1;
                    S.insert({spr.wartosc + 1,spr.y+1,spr.x});
                }
            }
        }
        if (spr.y - 1 >= 0)
        {
            if (plansza[spr.y-1][spr.x] == 0)
            {
                if (dp[spr.y-1][spr.x] == -1 || spr.wartosc + 1 < dp[spr.y-1][spr.x])
                {
                    auto it = S.find({dp[spr.y-1][spr.x],spr.y-1,spr.x});
                    if (it != S.end())
                        S.erase(*it);
                    dp[spr.y-1][spr.x] = spr.wartosc + 1;
                    S.insert({spr.wartosc + 1,spr.y - 1,spr.x});
                }
            }
        }
    }

    cout << "NIE" << '\n';

    return 0;
}

2 - zauważ, że w tym przypadku masz graf o krawędziach z wagami 1 oraz 0, więc możesz na deque zoptymalizować Dijkstrę, korzystając z tego, że wartości na deque nie będą różnić się o więcej niż 1! W zależności od tego czy przetwarzasz o wadze 0 czy o wadze 1, to dodajesz na początek / koniec deque. Usuniemy loga - O(N*M) - też wzorcówka

Tu masz kod:

#include <iostream>
#include <vector>
#include <deque>

using namespace std;

struct Element_seta
{
    int wartosc;
    int y;
    int x;
    bool operator < (const Element_seta &element_seta) const
    {
        if (wartosc == element_seta.wartosc)
        {
            if (y == element_seta.y)
                return x < element_seta.x;
            return y < element_seta.y;
        }
        return wartosc < element_seta.wartosc;
    }
};

int n = 0, m = 0, wczytana_liczba = 0;
vector<vector<int>> plansza;
vector<vector<int>> dp;
deque<Element_seta> S;
vector<vector<bool>> czy_sprawdzilismy;

int main()
{
    // O(N*M)
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    for (int i = 0; i < n; ++i)
    {
        plansza.push_back({});
        dp.push_back({});
        czy_sprawdzilismy.push_back({});
        for (int j = 0; j < m; ++j)
        {
            cin >> wczytana_liczba;
            plansza[i].push_back(wczytana_liczba);
            dp[i].push_back(-1);
            czy_sprawdzilismy[i].push_back(false);
        }
    }

    for (int i = 0; i < n; ++i)
    {
        // Dodajemy wierzcholki startowe na seta.
        if (plansza[i][0] == 0)
        {
            S.push_back({0,i,0});
            dp[i][0] = 0;
        }
    }

    while(!S.empty())
    {
        Element_seta spr = S.front();
        S.pop_front();
        if (czy_sprawdzilismy[spr.y][spr.x] == true)
            continue;
        if (spr.x == m-1)
        {
            cout << spr.wartosc << '\n';
            return 0;
        }
        // Krawedzie o wadze 0.
        if (spr.x + 1 < m)
        {
            if (plansza[spr.y][spr.x+1] == 0)
            {
                if (dp[spr.y][spr.x+1] == -1 || spr.wartosc < dp[spr.y][spr.x+1])
                {
                    dp[spr.y][spr.x+1] = spr.wartosc;
                    S.push_front({spr.wartosc,spr.y,spr.x+1});
                }
            }
        }
        // Krawedzie o wadze 1
        if (spr.y + 1 < n)
        {
            if (plansza[spr.y+1][spr.x] == 0)
            {
                if (dp[spr.y+1][spr.x] == -1 || spr.wartosc + 1 < dp[spr.y+1][spr.x])
                {
                    dp[spr.y+1][spr.x] = spr.wartosc + 1;
                    S.push_back({spr.wartosc + 1,spr.y+1,spr.x});
                }
            }
        }
        if (spr.y - 1 >= 0)
        {
            if (plansza[spr.y-1][spr.x] == 0)
            {
                if (dp[spr.y-1][spr.x] == -1 || spr.wartosc + 1 < dp[spr.y-1][spr.x])
                {
                    dp[spr.y-1][spr.x] = spr.wartosc + 1;
                    S.push_back({spr.wartosc + 1,spr.y - 1,spr.x});
                }
            }
        }
    }

    cout << "NIE" << '\n';

    return 0;
}

3 - Można napisać prostego BFS-a, gdzie robisz 2 rzeczy. Po 1 najpierw dodajesz na vector wierzcholki z 1 kolumny, i rozlewasz się każdym do przodu(w tym samym wierszu) i dodajesz je na vector. Potem po dodaniu iterujesz się po tym vectorze, i dodajesz na vector te do góry / dół z wagą o 1 większą. I tak w kółko - O(N*M) - wzorcówka.

Przypomniałem sobie o takim zdaniu z OIG-a: https://szkopul.edu.pl/problemset/problem/OwOeE32S_q-mlnooF5Od_eoy/site/?key=statement wchodzi na ten sam trik.

+2 głosów
odpowiedź 5 lutego 2023 przez adrian17 Mentor (354,880 p.)

Zacznijmy od tego że jak w każdym pytaniu dajesz na górze takiego potworka

int X, Y, DP[1001][1001];
bool GRAF[1001][1001], VIS[1001][1001];
queue <pair <int, int>> Q;
vector <pair <int, int>> ODW;

To dajesz sygnał że wcale nie chcesz żeby ktokolwiek zrozumiał co się dzieje w Twoim kodzie, a tym samym był w stanie Ci pomóc.

Natomiast... IMO potężnie przekombinowałeś zadanie? Zamiast robić pełnego BFSa z kolejką z każdego możliwego punktu startowego, można to zrobić w jednym "przejściu" od lewej do prawej - mając "najmniejsze odległości" dla jednej kolumny, możesz wywnioskować "najmniejsze odległości" dla kolejnej i powtarzać to aż do końca.

1
komentarz 5 lutego 2023 przez polandonion Dyskutant (7,700 p.)
poprawiłem kod na ideone dodając opisy

mi się wydaje, że program wykłada się na teście, gdzie są same zera
1
komentarz 5 lutego 2023 przez Whistleroosh Maniak (57,400 p.)

Wywali się na samych zerach, bo za kazdym razem będziesz czyścił wszystkie X*Y pól. Natomiast zgadzam się z pomysłem @adrian17, żeby przejść kolumnami i wtedy da się to rozwiązać w O(X*Y)

1
komentarz 5 lutego 2023 przez polandonion Dyskutant (7,700 p.)
kurcze, spróbowałem zrobić waszym sposobem i albo źle zaimplementowałem albo źle działa, bo dla 4 testów błędna odpowiedź i jeden test (ciągle ten sam) wywala się przez czas.

kod: https://ideone.com/Hzlox8
1
komentarz 5 lutego 2023 przez Whistleroosh Maniak (57,400 p.)
Jeśli dostajesz TLE to pewnie przez tę trzecią zagnieżdzoną pętlę. Ogólnie kod wygląda podejrzanie, nie rozumiem co tam się dzieje. Żeby policzyć dp[x][y] musisz wziąć min z dp[x-1][y] i dp[x-1][j] + 1 dla takich j, że możesz dostać się z pola (x, y) do pola (x, j)
1
komentarz 5 lutego 2023 przez polandonion Dyskutant (7,700 p.)
teraz juz nie ma przekroczenia czasu, ale za to tam gdzie bylo to mam blad + stare bledy, co daje razem 40% wyniku: https://ideone.com/Hzlox8
1
komentarz 5 lutego 2023 przez Whistleroosh Maniak (57,400 p.)
Znowu masz ten sam błąd co poprzednio. Musisz liczyć dp[x][y] na podstawie dp[x-1][...]
1
komentarz 5 lutego 2023 przez polandonion Dyskutant (7,700 p.)
juz dziala, dodalem druga identyczna petle, tylko zamiast od 1...Y to od Y...1 i dziala na 100%
1
komentarz 5 lutego 2023 przez polandonion Dyskutant (7,700 p.)
i juz rozumiem czegu bfs nie daje rady, dzieki chlopaki za pomoc i zycze milego popoludnia
komentarz 6 lutego 2023 przez pasjonat_algorytmiki Pasjonat (19,540 p.)

@adrian17, 

"Zacznijmy od tego że jak w każdym pytaniu dajesz na górze takiego potworka"

Strzał za 1000 punktów, lepiej bym tego nie ujął. Nawet nie chodzi o te pierwsze 2 linijki, tylko 2 kolejne. Jak robisz paira jakiegoś, i dajesz go do debugowania, to musisz opisywać co to first, co to second!

Podobne pytania

0 głosów
1 odpowiedź 1,574 wizyt
pytanie zadane 12 lutego 2023 w C i C++ przez polandonion Dyskutant (7,700 p.)
0 głosów
1 odpowiedź 855 wizyt
+1 głos
1 odpowiedź 809 wizyt
pytanie zadane 7 czerwca 2023 w C i C++ przez Szyszka Gaduła (3,530 p.)

93,718 zapytań

142,630 odpowiedzi

323,262 komentarzy

63,265 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...