Zoznam úloh

2. Sssssss, tak robia hady

Zadanie

Martin sa veľmi dobre pozná s ostatnými gréckymi bohmi. Raz sa však rozprával so svojím starým známym HADesom, ktorý počas celej noci strážil brány Olympu. Po dlhej šichte z neho vyliezlo iba:

Sssssssssss SSSsssS SSssssssssSS SSSSSSS SSSSsSSss?! sssssssSS ssSSSSSSSsSS SSSSSSSSs ssssssss sssSSSSSSS SSSSSSS SSSSsssS. SSSS SSSSSssss SSSssssSSS SSSsssSSSss sSSssss?

Určite každý z vás ovláda hadie jazyky, Pythony a iné Kobry, ale pre tých z vás, ktorí zadaniu vyššie nerozumejú, je tu aj jeho preklad do reči smrteľníkov.

Zadanie:

Vašou úlohou je odpovedať na základe pohybu hadíka v mriežke s rozmermi $m \times n$, na koľkých políčkach mohol začať bez toho, aby vyšiel mimo mriežku. Hadík sa každým kolom zväčšuje a pohybuje sa do jedného zo štyroch základných smerov. Ak je postupnosť na vstupe neplatná (hadík narazil sám do seba alebo vyšiel mimo mriežku), odpovedzte 0.

Formát vstupu

V prvom riadku vstupu dostanete dve čísla $m$ - počet stĺpcov, $n$ - počet riadkov, Nasleduje 1 riadok v ktorom dostanete reťazec dĺžky $t$ obsahujúci znaky: ‘H’,‘D’,‘P’,‘L’ - reprezentujúce smery: hore, dole, vpravo, vľavo.

V sadách platia nasledujúce obmedzenia:

Sada 1 2 3 4
$1 \leq m, n \leq$ $20$ $10^3$ $10^3$ $10^9$
$1 \leq t \leq$ $20$ $10^3$ $10^5$ $10^5$

V sadách 1, 2 navyše platí že hadík sa pohybuje iba dole a doprava.

Formát výstupu

Na jeden riadok vypíšte jedno číslo - na koľkých políčkach mohol had začať. Keďže výsledok môže byť fakt veľký nezabudnite použiť long long v C++

Príklady

Input:

3 3
DDD

Output:

0

Nezáleží, kde hadík začne v mriežke, vždy zíde o 3 políčka nižšie (mimo mriežku)

Input:

4 3
DDPPHLL

Output:

0

Hadík narazil sám do seba

Input:

13 13
PDDDPDDDPPPDPPPPPDDD

Output:

9

Riešenie hrubou silou

Úlohu vieme naivne riešiť pomocou hrubej sily. Pre každú súradnicu, kde mohol had začať, môžeme odsimulovať pohyb hada nasledovným spôsobom. Každým krokom si vypočítame zmenu súradníc:

  1. H: y+=1

  2. D: y-=1

  3. P: x+=1

  4. L: x-=1

Skontrolujeme či sme nevyšli z mriežky, teda skontrolujeme či platia nerovnosti: 1. $0 \leq x \leq m$ 2. $0 \leq y \leq n$

Ešte nám zostáva skontrolovať či had nerazil sám do seba. To vieme napríklad takto.

Zakaždým si uložíme pár súradníc do listu a skontrolujeme či sa tam už nenachádzali predtým. Ak sa v liste už súradnice nachádzajú, tak had narazil sám do seba.

Optimálne riešenie

Základným pozorovaním v úlohe je, že had sa pohybuje iba v obdĺžniku s rozmermi $a,b$. V úlohe sa teda pýtame koľkými spôsobmi vieme umiestniť tento obdĺžnik do mriežky zo zadania. Zároveň platí, že ak had narazí sám do seba nebude to závisieť od pozície obdĺžnika a narazí zakaždým. Pri kontrole narazení hada do samého seba je však potrebné použiť dátovú štruktúru set (unordered). Takto budeme môcť vkladať súradnice a kontrolovať ich výskyt v časovej zložitosti O(1).1

Rozmery tohto obdĺžnika získame tak, že si budeme pamätať minimum a maximum z týchto hodnôt a neskôr ich od seba odčítame.

Počet možností ako vieme umiestniť tento obdĺžnik do mriežky zo zadania vieme vypočítať ako $(m-a) \times (n-b)$ kde:

$a = max_x - min_x$

$b = max_y - min_y$

Umiestnenie obdĺžnika si vieme predstaviť ako umiestnenie ľavého horného bodu obdĺžnika, tak aby pravý dolný bod nevyčnieval z mriežky.

Časová a pamäťová zložitosť

Takéto riešenie bude mať časovú zložitosť $O(t)$ a pamäťovú zložitosť $O(t)$

Všetky set-ové operácie, ktoré vykonávame $t$-krát budú v O(1) čase. Kvôli kontrole kolízie hada, si budeme potrebovať pamätať všetkých $t$ súradníc.

Plné body môžete získať aj keď využijete ordered set.

N, M = map(int, input().split())
S = input()

cur = (0, 0)
vis = set()
maxx, minx, maxy, miny = 0, 0, 0, 0

for ch in S:
    if ch == "D":
        cur = (cur[0]-1, cur[1])
    if ch == "H":
        cur = (cur[0]+1, cur[1])
    if ch == "P":
        cur = (cur[0], cur[1]+1)
    if ch == "L":
        cur = (cur[0], cur[1]-1)

    maxx = max(maxx, cur[0])
    minx = min(minx, cur[0])
    maxy = max(maxy, cur[1])
    miny = min(miny, cur[1])

    if cur in vis:
        print(0)
        exit()

    vis.add(cur)

width = maxx-minx
height = maxy-miny

if (M-width) <= 0 or (N-height) <= 0:
    print(0)
    exit()

print((M-width)*(N-height))
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    cin.tie(0)->sync_with_stdio(0);

    ll m, n;
    cin >>m>>n;
    string s;
    cin >>s;

    set<pair<int,int>> visited;
    ll x = 0; ll y = 0;
    bool narazil = false;

    ll max_x = 0; ll min_x = 0; ll max_y = 0; ll min_y = 0;
    for(int i = 0; i < s.size(); i++){
        if(s[i] == 'H'){
            y++;
        }else if(s[i] == 'D'){
            y--;
        }else if (s[i] == 'P'){
            x++;
        }else{
            x--;
        }

        max_x = max(x, max_x);
        min_x = min(x, min_x);
        max_y = max(y, max_y);
        min_y = min(y, min_y);

        if(visited.count({x,y}) > 0) {
            narazil = true;
        }
        visited.insert({x,y});
    }

    if (narazil or max_x - min_x > m or max_y - min_y > n){
        cout << "0\n";
    }else{
        cout << (m - (max_x-min_x)) * (n - (max_y-min_y))<< '\n'; 
    }

}

  1. Ak by ste sa chceli dozvedieť viac o dátovej štruktúre set, odporúčam: https://www.geeksforgeeks.org/introduction-to-set-data-structure/ 

Pre odovzdávanie sa musíš prihlásiť.
Trojsten

Korešpondenčný seminár z programovania zastrešuje občianske združenie Trojsten.

Kontakt
Ďalšie projekty