Matematická olympiáda – kategorie P

Řešení úloh ústředního kola (1. soutěžní den) 47. ročníku

P-III-1 Jednotková podmatice

Nechť [i,j] jsou souřadnice některého prvku matice A. Potom tento prvek je pravým dolním rohem jednotkové podmatice velikosti d>0 právě tehdy, jsou-li splněny následující podmínky:
Označme jako m[i,j] velikost největší jednotkové podmatice, která má pravý dolní roh v prvku A[i,j]. Pokud A[i,j]=0, pak jistě bude m[i,j] = 0. Z technických důvodů dodefinujeme hodnoty m[i,0] = m[0,i] = 0 pro všechna i=1,2,...,N. Umožní nám to jednodušší zápis následující přesné definice hodnot m[i,j].

Nechť souvislý úsek nul (ukončený jedničkou nebo okrajem matice) nalevo od A[i,j] má délku B a souvislý úsek nul nad A[i,j] má délku C. Potom:
m[i,j] = 0, jestliže A[i,j]=0
m[i,j] = min{m[i-1,j-1], B, C} + 1, jestliže A[i,j]=1
Hodnoty B a C můžeme pro všechny prvky matice A snadno spočítat a uložit do pomocných matic při jediném průchodu v čase O(n2). Potom již můžeme postupně počítat všechny hodnoty m[i,j]. Budeme-li postupovat například po řádcích shora dolů, budeme mít při výpočtu m[i,j] už k dispozici hodnotu m[i-1,j-1]. Stačí tedy na základě známých hodnot m[i-1,j-1], B, C a A[i,j] v konstantním čase určit hodnotu m[i,j]. Celková časová složitost algoritmu je tudíž O(n2).

Právě popsané řešení úlohy nyní ještě vylepšíme z hlediska paměťové náročnosti. Hodnoty B a C totiž není třeba počítat předem pro všechny prvky matice A a ukládat je do pomocných matic. Hodnotu B si můžeme vždy průběžně počítat při průchodu řádkem matice zleva doprava a udržovat si ji v jedné pomocné proměnné (tato hodnota vůbec nezávisí na ostatních řádcích matice). Hodnoty C se snadno počítají na základě hodnot C v předchozím řádku, stačí nám tedy použít pomocné jednořádkové pole. Hodnoty m[i,j] si budeme ukládat přímo do matice A.

program Jednotkova_podmatice; {v dané matici z 0/1 hledá maximální jednotkovou podmatici} const MaxN = 20; {maximální velikost matice} var A: array[0..MaxN, 0..MaxN] of integer; {matice} C: array[1..MaxN] of integer; N: integer; {velikost matice} Max: integer; {výsledek} B, i, j:integer; function Min(a, b, c: integer): integer; {minimum ze tří čísel} begin if b < a then a := b; if c < a then a := c; Min := a; end; {function Min} begin writeln; write('Velikost zkoumané matice: '); readln(N); writeln('Prvky matice po řádcích - pouze 0/1:'); for i:=1 to N do for j:=1 to N do read(A[i,j]); A[0,0] := 0; for i:=1 to N do begin A[i,0] := 0; A[0,i] := 0; C[i] := 0 end; Max:=0; for i:=1 to N do {cyklus pro řádky} begin B := 0; for j:=1 to N do {cyklus pro sloupce} if A[i,j]=0 then begin B := B+1; {souvislý úsek nul v řádku} C[j] := C[j]+1; {souvislý úsek nul v sloupci} end else {A[i,j]=1} begin A[i,j] := Min(A[i-1,j-1], B, C[j]) + 1; if A[i,j] > Max then Max := A[i,j]; B := 0; C[j] := 0; end; end; writeln; writeln('Velikost strany maximální jednotkové podmatice: ',Max) end.

P-III-2 Kódy

Následující kód řeší obě podúlohy (nikoliv však současně - není schopen rozhodnout, jestli došlo k opravitelné jednochybě nebo neopravitelné dvojchybě).

Nechť zpráva má n=2k-1 bitů označených b1..bn (v případě, že je kratší, považujeme zbylé bity za nuly). My ji doplníme o paritní bit p (stejně jako v ukázkovém příkladu v zadání) a zabezpečovacími bity c0 až ck-1, jež budou paritními bity jednotlivých bloků zvolených podle následujícího pravidla: v i-tém bloku budou právě ty bity, jejichž pořadové číslo (pro bit bz to je z) má v i-tém binárním řádu jedničku (každý bit původní zprávy tak patří do alespoň jednoho bloku a je jednoznačně identifikován tím, do kterých bloků patří a do kterých nikoliv).

Úloha a

Po příjmu zprávy zkusíme podle přijatých datových bitů znovu spočíst všechny bity kontrolní (tedy p a všechna ci). Pokud souhlasí s těmi, které byly přijaty, zpráva byla přijata bezchybně nebo došlo k více jak jedné chybě. Pokud došlo k právě jedné chybě, mohlo k ní dojít těmito způsoby:

Úloha b

Nedošlo-li k žádné chybě, souhlasí všechny zabezpečovací bity s jejich vypočtenými hodnotami (viz řešení úlohy a). Došlo-li k právě jedné chybě, nesouhlasí centrální paritní bit nebo jeden z blokových paritních bitů (viz diskuse výše). Došlo-li k právě dvěma chybám, mohou nastat následující případy:
Ve všech případech tedy poznáme, že k chybě došlo.

P-III-3 Kombinační sítě

Nechť A= {0,1,2,3,4,5,6,7,8,9,@} je abeceda naší sítě. Pak pro všechny symboly s, prvky A, označíme C <= s počet vstupů Ai, na nichž je hodnota menší nebo rovná s (jelikož C <= s <= n, lze všechna C <= s vyjádřit ve dvojkové soustavě nějakými bity C <= s,1 ... m, kde m=[log2 n]). Výstup Bi pak bude mít hodnotu s právě tehdy, je-li s největší prvek abecedy, pro nějž platí C <= s <= i. Jinými slovy: setříděná posloupnost vypadá tak, že nejdříve obsahuje C <= 0 nul, pak až do pozice C <= 1 jedničky ... až do pozice C <= 9=n devítky.

Pro realizaci tohoto algoritmu kombinační sítí budeme využívat následující podsítě:

Nejprve spočteme pomocné proměnné X <= s,i = TEST <= (s,Xi) mající hodnotu 1 právě tehdy, je-li Xi <= s, jinak 0 (to zvládneme sítí hloubky 1). Rozšíříme-li každou z těchto proměnných nulami na m-bitové číslo X <= s,i,1 ... m, je zjevně C <= s součtem čísel X <= s,i přes všechna i. Pro určení tohoto součtu stačí využít asociativity sčítání a nejdříve sečíst všechny dvojice X <= s,2k + X <= s,2k+1, pak dvojice těchto dvojic atd. (viz hledání minima a maxima v domácím kole, kde se používá tentýž trik), čimž získáme konečný součet C <= s sítí o m úrovních, z nichž každá se skládá z m-bitových sčítaček (ADDm) hloubky O(log m), tedy celková hloubka sítě určující C <= s je O(m log m)=O(log n loglog n).

Poté spočteme pro každou výstupní pozici i a každý symbol abecedy s hodnoty Zs,i = CMPm(C <= s, i) (hloubka O(log m)) a definujeme Bi = FIRST(Z0,i, ..., Z9,i) jako výstupy sítě (hloubka O(1)). Tedy Bi je nejmenší s takové, že CMPm(C <= s,i) <> 1, tudíž nejmenší takové, pro nějž C <= s >= i, což je přesně hodnota odvozená v prvním odstavci.

Síť se skládá ze čtyř částí hloubek po řadě O(1), O(log n loglog n), O(loglog n) a O(1), její celková hloubka proto je O(log n loglog n).