Zadanie
Dušan sa rozhodol usporiadať veľkú párty v T2 - obľúbenej matfyzáckej miestnosti. T2 bola známa svojou skvelou hudbou a atmosférou, a každý túžil byť súčasťou jej legendárnych večierkov.
T2 je však príliš malá na to, aby sa do nej zmestilo viac ako 12 ľudí. Rozhodol sa, že odstráni jednu zo stien, nech vytvoria väčšiu miestnosť. Aby sa však uistili, že vyberú tú správnu stenu, obrátili sa na vás. Pomôžte im ju nájsť!
Úloha
Máte k dispozícii rozloženie budovy znázornené mriežkou stien a prázdnych miest. Vašou úlohou je odstrániť najviac jednu stenu tak, aby ste vytvorili miestnosť s najväčším možným obsahom.
Avšak:
- steny na hraniciach budovy nemožno odstraňovať,
- búrať možno len steny reprezentované znakmi
'-'
a'|'
, - len znaky
' '
sa započítavajú do plochy miestnosti.
Vstup
Na prvom riadku vstupu dostanete dve celé čísla \(n\) a \(m\), kde \(n\) predstavuje
počet riadkov a \(m\) predstavuje počet
stĺpcov v budove. Ďalej dostanete 2D mriežku o veľkosti \(2n+1\) riadkov a \(2m+1\) stĺpcov, ktorá reprezentuje
rozloženie budovy. Mriežka môže obsahovať nasledujúce znaky:
'+'
reprezentuje roh,'-'
reprezentuje vodorovnú stenu,'|'
reprezentuje zvislú stenu,'.'
reprezentuje ‘nestenu’, ktorá sa neráta do obsahu miestnosti, rovnako ako steny (je to len oddeľovač),' '
reprezentuje prázdne miesto, ktoré sa ráta do obsahu, má obsah 1,'#'
reprezentuje steny budovy (nebúrateľné).
Výstup
Vypíšte jedno celé číslo reprezentujúce maximálnu plochu nejakej miestnosti, ktorú možno dosiahnuť odstránením najviac jednej steny.
Obmedzenia
V prvej sade budú budovy dlhé \(1 \times n\) chodby. V druhej sade budú budovy malé \(4 \times 4\) pivnice.
Sada | \(1\) | \(2\) | \(3\) | \(4\) |
---|---|---|---|---|
\(1 \leq n \leq\) | \(1\) | \(4\) | \(100\) | \(787\) |
\(1 \leq m \leq\) | \(1000\) | \(4\) | \(100\) | \(787\) |
Príklad
Input:
4 6
#############
# . . | | | #
#.+-+.+.+.+.#
# | | | . . #
#-+.+-+.+-+.#
# . | . . | #
#.+-+.+.+-+.#
# . . . . . #
#############
Output:
24
V danom príklade môžeme odstrániť zvislú stenu '|'
na pozícii (x=3, y=2)
a spojiť dve miestnosti s plochami 19
a 5, čím vytvoríme najväčšiu miestnosť s plochou 24. Aktualizovaná
mriežka po odstránení steny by vyzerala následovne:
#############
# . . | | | #
#.+-+.+.+.+.#
# X | | . . #
#-+.+-+.+-+.#
# . | . . | #
#.+-+.+.+-+.#
# . . . . . #
#############
X označuje odstránenú stenu.
Síce
vstup vyzerá strašidelne, ale to vôbec nevadí, načítame ho tak ako je.
Keďže budeme hľadať obsahy miestností, potrebujeme presný plán budovy.
Keďže každé prázdne políčko (' '
) má obsah 1, tak nám pre
každú miestnosť stačí zrátať, koľko je v nej takých prázdnych políčok.
Nakoniec len vezmeme dve susedné miestnosti, ktoré nám po ich spojení
dajú najväčší možný obsah. Jednoduché, však? Ale poďme na to
postupne.
Hor sa prvú sadu!
Keďže v prvej sade sú všetky miestnosti na jednom riadku, môžeme
postupne prechádzať druhým riadkom vstupu a zaradom počítať obsahy
jednotlivých miestností. Takže, spravíme si pole, kam si budeme postupne
tieto obsahy ukladať a prechádzame vstup – ak narazíme na stenu
('|'
), pridáme do poľa s obsahmi 0. Ak prídeme na prázdne
políčko (' '
) pripočítame k poslednému číslu v poli 1. Inak
povedané, kým sme v \(i\)-tej
miestnosti, tak k \(i\)-temu číslu v
poli prirátavame 1 za každé prázdne políčko. Keď narazíme na stenu,
vieme že nám skončila miestnosť a bude nasledovať ďalšia. Akurát, na
začiatku nemusí byť po '#'
hneď stena, takže si na začiatok
musíme dať “umelo” do poľa s obsahmi 0. Nakoniec už iba prejdeme toto
pole obsahov, nájdeme 2 susedné miestnosti s najväčším súčtom a tento
súčet vypíšeme.
Ostatné sady
Ako vyrátať obsah miestnosti?
Na vyrátanie obsahu nejakej miestnosti nám stačí začať prehľadávanie do hĺbky (DFS) z nejakého bodu vnútri tejto miestnosti a zrátať počet políčok, cez ktoré prejdeme. Toto budeme v najhoršom prípade musieť urobiť pre každý bod v budove, teda v čase \(O(nm)\).
Pomalé riešenie
Postupne prejdeme všetkými zbúrateľnými stenami a vypočítame obsah miestnosti napravo a naľavo, resp. hore a dole, podľa toho aký typ steny sme zbúrali. (!!!POZOR, treba ošetriť prípad, v ktorom je z oboch strán steny rovnaká miestnosť. Po zbúraní takejto steny ostane obsah miestnosti nezmenený!!!) Súčet týchto obsahov je obsah misetnosti, ktorá vznikne po zbúraní tejto steny. Doterajší najväčší obsah si budeme pamätať a po preskúmaní všetkých zbúrateľných stien vypíšeme najväčší obsah miestnosti po zbúraní nejakej steny. Časová zložitosť tohoto riešenia je \(O((nm)^2)\), pretože pre každú zbúrateľnú stenu, ktorých je rádovo nm, sme rátali obsah susedných miestností, čo je rádovo taktiež \(nm\).
Pamäťová zložitosť tohoto riešenia je \(O(nm)\), pretože si potrebujeme pamätať vstup.
Optimálne riešenie
Všimnime si, že obsah nejakej miestnosti počítame toľkokrát, koľko má zbúrateľných stien, aj napriek tomu, že jej obsah sa za celý čas nezmenil.
Preto ešte predtým ako budeme iterovať zbúrateľnými stenami, si predpočítame obsahy daných miestností.
Potom, keď budeme iterovať cez všetky zbúrateľné steny, si vieme vyrátať obsah miestnosti po zbúraní steny v \(O(1)\).
Predpočítať si obsahy miestností vieme znova pomocou DFS, no skôr ako začneme, musíme ešte: - Vytvoriť pole(1), do ktorého si budeme ukladať už vypočítané obsahy miestností. - Vytvoriť 2D pole(2), ktoré bude mať rozmery ako plánik budovy. V ňom si do každého bodu zapíšeme, na ktorom indexe v poli(1) je uložený obsah miestnosti, ktorá obsahuje tento bod.
Teraz už môžeme predpočítavať. Všimnime si, že skutočne prejdeme každým bodom v plániku nanajvýš raz, nakoľko nemusíme spúšťať DFS (hľadanie obsahu) z bodov, pre ktoré už v poli(2) existuje nejaký index. Preto bude mať táto časť algoritmu časovú zložitosť \(O(nm)\).
Časová zložitosť tohoto riešenia je \(O(nm)\), pretože sme najprv predpočítali obsahy miestností v \(O(nm)\), nakoľko sme prešli celý obsah budovy, a potom pre každú zbúrateľnú stenu, ktorých je rádovo \(nm\), sme rátali obsah susdených miestností v \(O(1)\). Pamäťová zložitosť je \(O(nm)\), pretože si potrebujeme pamätať vstup (nm), pole(1) (nanajvýš nm), pole(2) (nm).
O algoritme DFS sa môžete dozvedieť viac tu: https://www.ksp.sk/kucharka/dfs/
# ulozim obsahy zistene dfs do pola group
# prejdem vsetky policka, a skusam rozbijat steny
# pozriem okolo nej a poscitavam obsahy, ak este neboli zapocitane
import sys
10**6)
sys.setrecursionlimit(
= map(int, input().split())
n, m = [[j for j in input()] for _ in range(2 * n + 1)]
inp
= [[0 for _ in range(2 * m + 1)] for _ in range(2 * n + 1)]
group = 1
curr_group = [0, 0]
areas
def inside(i, j):
return 0 <= i < 2 * n + 1 and 0 <= j < 2 * m + 1
def dfs(i, j):
= curr_group
group[i][j] += 1
areas[curr_group]
# vnor sa hlbsie, ked je to vnutri
if inside(i + 2, j) and group[i + 2][j] == 0 and inp[i + 1][j] == ".":
+ 2, j)
dfs(i if inside(i - 2, j) and group[i - 2][j] == 0 and inp[i - 1][j] == ".":
- 2, j)
dfs(i if inside(i, j + 2) and group[i][j + 2] == 0 and inp[i][j + 1] == ".":
+ 2)
dfs(i, j if inside(i, j - 2) and group[i][j - 2] == 0 and inp[i][j - 1] == ".":
- 2)
dfs(i, j
return
for i in range(1, 2 * n + 1, 2):
for j in range(1, 2 * m + 1, 2):
if inp[i][j] == " " and group[i][j] == 0:
dfs(i, j)+= 1
curr_group 0)
areas.append(
= max(areas)
ans for i in range(1, 2 * n):
for j in range(1, 2 * m):
if inp[i][j] == "|":
if group[i][j - 1] != group[i][j + 1]:
= max(ans, areas[group[i][j - 1]] + areas[group[i][j + 1]])
ans elif inp[i][j] == "-":
if group[i - 1][j] != group[i + 1][j]:
= max(ans, areas[group[i - 1][j]] + areas[group[i + 1][j]])
ans
print(ans)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
.tie(0)->sync_with_stdio(0);
cin
int N, M;
>> N >> M;
cin .ignore();
cin
int Vn = N * M;
<vector<int>> Eex(Vn), Erm(Vn);
vectorauto convert = [M](int x, int y) { return y * M + x; };
for (int y = 1; y < 2 * N + 1; y += 2) {
, lcurr;
string labove(cin, labove);
getline(cin, lcurr);
getlinefor (int x = 1; x < 2 * M + 1; x += 2) {
int v = convert(x / 2, y / 2);
int vx = convert(x / 2 - 1, y / 2);
int vy = convert(x / 2, y / 2 - 1);
if (x > 1) {
if (lcurr[x - 1] == '.') {
[v].push_back(vx);
Eex[vx].push_back(v);
Eex} else {
[v].push_back(vx);
Erm[vx].push_back(v);
Erm}
}
if (y > 1) {
if (labove[x] == '.') {
[v].push_back(vy);
Eex[vy].push_back(v);
Eex} else {
[v].push_back(vy);
Erm[vy].push_back(v);
Erm}
}
}
}
<int> C(Vn, -1);
vectorauto dfs = [&](auto&& dfs, int v, int c) -> void {
[v] = c;
Cfor (int u : Eex[v])
if (C[u] == -1)
(dfs, u, c);
dfs};
<int> Sz;
vectorfor (int v = 0; v < Vn; ++v) {
if (C[v] == -1) {
(dfs, v, Sz.size());
dfs.push_back(0);
Sz}
++Sz[C[v]];
}
int res = *max_element(Sz.begin(), Sz.end());
for (int v = 0; v < Vn; ++v)
for (int u : Erm[v])
if (C[v] != C[u])
= max(res, Sz[C[v]] + Sz[C[u]]);
res
<< res << '\n';
cout }
Diskusia
Tu môžte voľne diskutovať o riešení, deliť sa o svoje kusy kódu a podobne.
Pre pridávanie komentárov sa musíš prihlásiť.