Sunteți pe pagina 1din 45

Ministerul Educaiei al Republicii Moldova

Olimpiada Republican la Informatic


Ediia 2010



























Chiinu 2010
2


Olimpiada Republican la Informatic. Ediia 2010.
Lucrarea conine problemele propuse la Olimpiada Republican la Informatic a elevilor, ediia 2010. Pentru
fiecare problem sunt descrise algoritmul i soluia respectiv n limbajul de programare PASCAL.
Materialele Olimpiadei pot fi de un real folos la studierea Informaticii att elevilor, ct i profesorilor de
Informatic.





La elaborarea ediiei au contribuit:

Anatol Gremalschi, doctor habilitat, profesor universitar, UTM
Ion Bolun, doctor habilitat, profesor universitar, ASEM
Iurie Mocanu, Ministerul Educaiei
Viorel Afteni Ministerul Educaiei
Dumitru Codreanu, Universitatea Bucureti
Dumitru Ciubati, Universitatea Bucureti
Bogdna Denis Universitatea Tehnic, Iai
Constantin Dolghieru, Universitatea Politehnic, Bucureti

3






Cuprins

CLASELE 7 9 .................................................................................................................... 4
EXPRESII .................................................................................................................................... 5
IEPURAII ................................................................................................................................... 7
SEGMENTE ............................................................................................................................... 11
PTRATE .................................................................................................................................. 15
NUMERE .................................................................................................................................. 17
LUPTA MARITIM ...................................................................................................................... 19
CLASELE 10 12 .............................................................................................................. 22
VSLAII .................................................................................................................................. 23
IEPURAII ................................................................................................................................. 26
SEGMENTE ............................................................................................................................... 30
ARPELE .................................................................................................................................. 35
DESCOMPUNERI ........................................................................................................................ 39
RADIOUL .................................................................................................................................. 42



4





Clasele 7 9

Denumirea
problemei
Numrul de
puncte alocat
problemei
Denumirea
fiierului surs
Denumirea
fiierului de
intrare
Denumirea
fiierului de
ieire
Expresii 100
EXPRES.PAS
EXPRES.C
EXPRES.CPP
EXPRES.IN EXPRES.OUT
Iepuraii 100
IEPURE.PAS
IEPURE.C
IEPURE.CPP
IEPURE.IN IEPURE.OUT
Segmente 100
SEGMENT.PAS
SEGMENT.C
SEGMENT.CPP
SEGMENT.IN SEGMENT.OUT
Ptrate 100
PATRAT.PAS
PATRAT.C
PATRAT.CPP
PATRAT.IN PATRAT.OUT
Numere 100
NUMERE.PAS
NUMERE.C
NUMERE.CPP
NUMERE.IN NUMERE.OUT
Lupta maritim 100
LUPTA.PAS
LUPTA.C
LUPTA.CPP
LUPTA.IN LUPTA.OUT
Total 600 - - -

5
Expresii

Se consider expresiile booleene de forma:

A <s
1
> B <s
2
> C <s
3
> D = Q

unde A, B, C, D i Q sunt numere ntregi fr semn, iar s
1
, s
2
i s
3
operatorii aritmetici
+, - .

Exemple:
57 17 34 6 12 = + + ;
63 926 45 12 8 = + + ;
21 6 4 2 21 = + .

Sarcin. Elaborai un program care, cunoscnd valorile numerelor A, B, C, D i Q,
selecteaz, dac-i posibil, operatorii s
1
, s
2
i s
3
n aa mod, nct expresia boolean

A <s
1
> B <s
2
> C <s
3
> D = Q

s ia valoarea de adevr true.
Date de intrare. Fiierul text EXPRES.IN conine pe o singur linie numerele ntregi
A, B, C, D i Q, separate prin spaiu.
Date de ieire. Fiierul text EXPRES.OUT va conine pe o singur linie un ir format din
operatorii s
1
, s
2
i s
3
. Dac problema admite mai multe soluii, n fiierul de ieire se va scrie
doar una, oricare din ele. Dac problema nu are soluie, n fiierul de ieire se va scrie irul de
caractere ???.
Exemplu1 1.
EXPRES.IN EXPRES.OUT
2 6 34 17 47 -++

Exemplu1 2.
EXPRES.IN EXPRES.OUT
8 12 45 9 26 ???

Restricii.
9
10 , , , 0 s s D C B A . Timpul de execuie nu va depi 0,1 secunde.
Programul va folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea
denumirea EXPRES.PAS, EXPRES.C sau EXPRES.CPP.

Rezolvare
Conform restriciilor problemei,
9
10 , , , 0 s s D C B A . Prin urmare, pentru a evita
erorile de depire, variabilele A, B, C, D i Q trebuie declarate cu tipul de date longint.

Program ExpresiiBooleene;
{ Clasele 07-09 }

var Intrare, Iesire : text;
A, B, C, D, Q : longint;
S : string;
6

begin
{ citirea datelor de intrare }
assign(Intrare, 'EXPRES.IN');
reset(Intrare);
readln(Intrare, A, B, C, D, Q);
close(Intrare);

{ deschiderea fisierului de iesire }
assign(Iesire, 'EXPRES.OUT');
rewrite(Iesire);

S:='???';

if(A+B+C+D=Q) then S:='+++';
if(A+B+C-D=Q) then S:='++-';
if(A+B-C+D=Q) then S:='+-+';
if(A+B-C-D=Q) then S:='+--';
if(A-B+C+D=Q) then S:='-++';
if(A-B+C-D=Q) then S:='-+-';
if(A-B-C+D=Q) then S:='--+';
if(A-B-C-D=Q) then S:='---';

{ scrierea datelor de iesire }
writeln(Iesire, S);
close(Iesire);
end.

7
Iepuraii

n cartea sa Liber Abaci (Cartea Abacului), editat n anul 1202, marele matematician
Fibonacci (cunoscut i sub numele Leonardo Pisano) a rezolvat problema nmulirii
iepurailor, care const n calcularea numrului de iepurai ntr-o populaie ce evolueaz
pornind de la o pereche iniial. Conform acestei cri, dezvoltarea populaiei de iepurai se
descrie cu ajutorul irului 1, 1, 2, 3, 5, 8, 13, ... , denumit irul lui Fibonacci. Formal, acest ir
se definete cu ajutorul urmtoarelor formule:

1
1
= F , 1
2
= F , 2
3
= F , 3
4
= F , ...,
1 1 +
+ =
i i i
F F F , ... ,

unde F
i
reprezint numrul de perechi de iepurai n luna i.

Sarcin. Elaborai un program care calculeaz suma S a primelor n numere din irul lui
Fibonacci.
Date de intrare. Fiierul text IEPURE.IN conine pe o singur linie numrul ntreg n.
Date de ieire. Fiierul text IEPURE.OUT va conine pe singur linie numrul ntreg S.
Exemplu.
IEPURE.IN IEPURE.OUT
4 7

Restricii. 300 2 s s n . Timpul de execuie nu va depi 0,5 secunde. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
IEPURE.PAS, IEPURE.C sau IEPURE.CPP.

Rezolvare

n general, problema nu prezint dificulti de algoritmizare, ntruct suma S poate fi
calculat printr-o singur parcurgere a irului lui Fibonacci. Mai mult ca att, nu este necesar
nici memorarea ntregului ir, ntruct numrul curent poate fi calculat prin nsumarea doar a
celor dou numere precedente din ir.
Prin F1, F2 i F3 vom nota oricare trei numere consecutive din irul lui Fibonacci.
Evident, suma S poate fi calculat cu ajutorul urmtoarei secvene de algoritm:

F1:=1;
F2:=1;
S:=F1;
i:=2;
repeat
S:=S+F2;
F3:=F1+F2;
F1:=F2;
F2:=F3;
i:=i+1;
until i>n;

Implementnd aceast secven cu ajutorul declaraiilor de variabile

var S, F1, F2, F3 : integer;

8
prin experimente de calcul ne convingem, c ncepnd cu 20 > n , la calcularea valorilor
variabilelor n studiu au loc erori de depire.

Cei care cunosc alte tipuri de date, ar putea s ncerce declaraia

var S, F1, F2, F3 : longint;

din Turbo Pascal sau tipurile de date Longword, Int64, Qword din Object Pascal. ns i n
astfel de cazuri, pentru valorile mari ale lui n apar erori de depire.
Pentru a evita erorile de depire, vom reprezenta numerele mari S, F1, F2 i F3 prin
tablouri, formate din K elemente. Fiecare element al tabloului memoreaz cte o cifra a
numrului respectiv.






Dac n procesul adunrii a dou numere mari A i B, formate din cte K cifre,
transportul calculat n cazul nsumrii cifrelor A[1], B[1] i transportului din rangul precedent
difer de zero, are loc o depire i programul va semnaliza o eroare.
n programul ce urmeaz, pentru a realiza experimentul de calcul, n componena
procedurii Adunare a fost inclus urmtoarea secven de instruciuni:

if Transport=1 then
begin
writeln('Depasire');
readln;
end;

Evident, n condiiile problemei, aceast secven de instruciuni nu va fi executat nici
o dat numai atunci, cnd valoarea lui K va fi stabilit suficient de mare. Aceast valoare
poate fi determinat cu ajutorul experimentelor de calcul, atribuindu-i lui n valoarea limit
300 = n , aa cum este indicat n restriciile problemei.

Program Iepurasii;
{ Clasele 07-09 }
const K = 70; { numarul de cifre ale numerelor foarte mari }
type Cifra = 0..9;
Numar = array[1..K] of Cifra;
var n : integer;
S : Numar;

procedure Citeste;
{ Citeste numarul n din fisierul de intrare }
var Intrare : text;
begin
assign(Intrare, 'IEPURE.IN');
reset(Intrare);
readln(Intrare, n);
close(Intrare);
end; { Citeste }

procedure Scrie;
{ Scrie suma S in fisierul de iesire }
var Iesire : text;
j : integer;
1 2 3 ... K
9
begin
assign(Iesire, 'IEPURE.OUT');
rewrite(Iesire);
j:=1;
while S[j]=0 do j:=j+1;
while j<=K do
begin
write(Iesire, S[j]);
j:=j+1;
end; { while }
writeln(Iesire);
close(Iesire);
end; { Scrie }

procedure Unu(var A : Numar);
{ Atribuie numarului A valoarea 1 }
var j : integer;
begin
for j:=1 to K-1 do A[j]:=0;
A[K]:=1;
end; { Unu }

procedure Adunare(A, B : Numar; var C : Numar);
{ Calculeaza suma C:=A+B }
var Transport : Cifra;
j : integer;
q : 0..19;
begin
Transport:=0;
for j:=K downto 1 do
begin
q:=A[j]+B[j]+Transport;
if q<=9 then Transport:=0
else
begin
Transport:=1;
q:=q-10;
end;
C[j]:=q;
end; { for }
if Transport=1 then
begin
writeln('Depasire');
readln;
end;
end; { Adunare }

procedure Suma;
var i : integer;
F1, F2, F3 : Numar;
begin
Unu(F1);
Unu(F2);
S:=F1;
i:=2;
repeat
Adunare(F2, S, S);
Adunare(F1, F2, F3);
F1:=F2;
F2:=F3;
i:=i+1;
until i>n;
end; { Suma }

10
begin
Citeste;
Suma;
Scrie;
end.

ntruct suma S se calculeaz printr-o singur parcurgere a irului lui Fibonacci,
complexitatea temporal a programului este ) (nK O . Conform restriciilor problemei,
300 s n , iar 70 = K . Evident, pentru astfel de valori, timpul de execuie al programului va fi
cu mult mai mic dect 0,5 secunde.

11
Segmente

Se consider mulimea S, format din n segmente distincte, notate prin S
1
, S
2
, ..., S
i
, ...,
S
n
. Fiecare segment S
i
este definit prin coordonatele ntregi carteziene ) , (
i i
y x ' ' , ) , (
i i
y x ' ' ' ' ale
extremitilor sale.
Segmentele S
i
, S
j
se numesc
adiacente, dac ele au o extremitate
comun.
Segmentele S
i
, S
j
se numesc
conectate dac exist o succesiune
de segmente, care ncepe cu S
i
i se
termin cu S
j
i n care oricare dou
segmente consecutive sunt
adiacente.
De exemplu, segmentele S
1
, S
5

de pe figura alturat sunt conectate
ntruct exist o succesiune de
segmente consecutiv adiacente: S
1
,
S
3
, S
5
.
Submulimea de segmente Q, Q _ S, formeaz o reea, dac segmentele respective sunt
conectate ntre ele, fr a avea ns conexiuni cu segmentele din submulimea S \ Q.
n general, mulimea de segmente S poate avea mai multe reele.
De exemplu, mulimea de segmente de pe figura de mai sus conine 3 reele: {S
1
, S
2
, S
3
,
S
5
}, {S
4
} i {S
6
, S
7
}.
Sarcin. Elaborai un program, care, cunoscnd mulimea de segmente S, calculeaz
numrul de reele k.
Date de intrare. Fiierul text SEGMENT.IN conine pe prima linie numrul ntreg n.
Fiecare din urmtoarele n linii ale fiierului de intrare conine numerele ntregi
i i i i
y x y x ' ' ' ' ' ' , , , ,
separate prin spaiu. Linia 1 + i a fiierului de intrare conine coordonatele extremitilor
segmentului S
i
.
Date de ieire. Fiierul text SEGMENT.OUT va conine pe singur linie numrul ntreg k.
Exemplu.
SEGMENT.IN SEGMENT.OUT
7
1 7 4 8
2 6 4 8
4 8 7 4
2 4 9 7
5 2 7 4
8 3 11 8
8 3 13 1
3


Restricii. 250 1 s s n ; 000 32 , , , 000 32 s ' ' ' ' ' ' s
i i i i
y x y x . Timpul de execuie nu va
depi 1,5 secunde. Programul va folosi cel mult 32 Megaoctei de memorie operativ.
Fiierul surs va avea denumirea SEGMENT.PAS, SEGMENT.C sau SEGMENT.CPP.

12
Rezolvare

Vom construi consecutiv fiecare din eventualele reele dup cum urmeaz:
1. Iniial, stabilim } { :
1
S Q = i } { \ :
1
S S S = .
2. Examinnd fiecare segment din mulimea S, le selectm pe cele conectate cu
segmentele din Q i le trecem din S n Q. Dac astfel de segmente nu mai exist,
s-a obinut o reea.
3. n continuare, stabilim } { :
i
S Q = , unde S
i
este unul din segmentele rmase n
mulimea S i construim din nou o eventual reea.
4. Procesul de construire a reelelor se termin atunci, cnd mulimea S devine una
vid.
n programul ce urmeaz coordonatele extremitilor de segmente se memoreaz n
tablourile X1, Y1, X2, Y2. Adiacena segmentelor se verific cu ajutorul funciei booleene
SuntAdiacente, iar conectivitatea segmentelor din mulime S cu cele din mulimea Q cu
ajutorul funciei booleene EsteConectat.

Program Segmente;
{ Clasele 07-09 }

const nmax=250; { numarul maximal de segmente }

type Segment=1..nmax;

var Q, S : set of Segment;
X1, Y1, X2, Y2 : array [1..nmax] of integer;
n : 1..nmax; { numarul de segmente }
k : 0..nmax; { numarul de retele }

procedure Citeste;
var i : 1..nmax;
Intrare : text;
begin
assign(Intrare, 'SEGMENT.IN');
reset(Intrare);
readln(Intrare, n);
for i:=1 to n do
readln(Intrare, X1[i], Y1[i], X2[i], Y2[i]);
close(Intrare);
end; { Citeste }

procedure Scrie;
var Iesire : text;
begin
assign(Iesire, 'SEGMENT.OUT');
rewrite(Iesire);
writeln(Iesire, k);
close(Iesire);
end; { Scrie }

function SuntAdiacente(i, j : Segment) : boolean;
{ returneaza true daca segmentele i, j sunt adiacente }
begin
SuntAdiacente := ((X1[i]=X1[j]) and (Y1[i]=Y1[j])) or
((X1[i]=X2[j]) and (Y1[i]=Y2[j])) or
((X2[i]=X1[j]) and (Y2[i]=Y1[j])) or
((X2[i]=X2[j]) and (Y2[i]=Y2[j]));
end; { SuntAdiacente }

13
function EsteConectat(i : Segment) : boolean;
{ returneaza true daca segmentul i este conectat cu segmentele din Q }
label 1;
var j : Segment;
begin
EsteConectat := false;
for j:=1 to n do
if j in Q then
begin
if SuntAdiacente(i, j) then
begin
EsteConectat := true;
goto 1;
end; { if }
end; { if }
1: end; { EsteConectat }

procedure NumaraRetele;
label 2;
var i, j : Segment;
Indicator : boolean;
begin
{ formam multimea de segmente S }
S:=[];
for i:=1 to n do S:=S+[i];
k:=0;
{ numaram retelele }
repeat
k:=k+1;
{ includem in multimea Q un segment din S }
for i:=1 to n do
if i in S then
begin
Q:=[i];
S:=S-[i];
goto 2;
end; { if }
2: { includem in Q segementele conexe din S }
repeat
Indicator:=true;
for i:=1 to n do
if i in S then
begin
if EsteConectat(i) then
begin
Q:=Q+[i];
S:=S-[i];
Indicator:=false;
end; { if }
end; { if }
until Indicator;
until S=[];
end; { NumaraRetele }

begin
Citeste;
NumaraRetele;
Scrie;
end.

Din analiza procedurii NumaraRetele rezult c numrul de operaii cerut de algoritm
este de ordinul
3
n . Conform restriciilor problemei, 250 1 s s n . Prin urmare, numrul
14
necesar de operaii va fi de ordinul
8
10 , mrime comparabil cu capacitatea de prelucrare a
calculatoarelor din laboratorul de informatic.

15
Ptrate

Delia a decis sa taie o foaie dreptunghiular, liniata in ptrele, dimensiunile creia
sunt m n ptrele, n buci ptrate dup cum urmeaz:
1) iniial, efectund o singur tietur pe una din liniile caroiajului, ea taie din foaie o
bucat de form ptrat de dimensiuni maximal posibile;
2) n continuare, din fragmentul dreptunghiular rmas de foaie, efectund o singur
tietur pe una din liniile caroiajului, Delia taie din nou o bucat de form ptrat de
dimensiuni maximal posibile .a.m.d.;
3) procesul de tiere continu pn cnd fragmentul rmas de foaie va fi de form
ptrat.
Pentru exemplificare, pe desenul de mai jos, prin literele A, B, C, D i E sunt notate cele
cinci ptrate, obinute n rezultatul tierii.


Sarcin. Elaborai un program care, cunoscnd dimensiunile n i m ale foii
dreptunghiulare, calculeaz cte buci k de form ptrat va obine Delia.
Date de intrare. Fiierul text PATRAT.IN conine pe o singur linie numerele ntregi n
i m, separate prin spaiu.
Date de ieire. Fiierul text PATRAT.OUT va conine pe singur linie numrul ntreg k.
Exemplu.
PATRAT.IN PATRAT.OUT
5 8 5

Restricii. 50000 , 1 s s m n . Timpul de execuie nu va depi 1 secund. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
PATRAT.PAS, PATRAT.C sau PATRAT.CPP.

Rezolvare

Procesul de tiere poate fi simulat cu ajutorul urmtoarelor operaii:
1) selectam latura mai mare a foii;
2) micorarea dimensiunii acestei laturi cu valoare laturii mai mici a foii;
3) continum procesul pn cnd ambele laturi ale foii vor avea aceleai dimensiuni.
ntruct numerele n, m i k din enunul problemei pot lua valori mai mari dect
constanta predefinit MaxInt, n programul ce urmeaz ele sunt declarate cu tipul longint.

Ptratele obinute n rezultatil tierii Foaia iniial
1 2 3 4 5 6 7 8
1
2
3
4
5

1 2 3 4 5 6 7 8
1
2
3
4
5

A B
C
D
E
16
Program Patrate;
{ Clasele 07-09 }
var
n, m, k : longint;
Intrare, Iesire : text;
begin
{ citirea datelor de intrare }
assign(Intrare, 'PATRAT.IN');
reset(Intrare);
readln(Intrare, n, m);
close(Intrare);
{ calcularea numarului de patrate }
k:=1;
while n<>m do
begin
if n>m then n:=n-m else m:=m-n;
k:=k+1;
end; { while }
{ scrierea datelor de iesire }
assign(Iesire, 'PATRAT.OUT');
rewrite(Iesire);
writeln(Iesire, k);
close(Iesire);
end.


Numrul de iteraii din instruciunea while nu poate depi valoare m n , care,
conform restriciilor problemei este de ordinul 10
10
, mrime comparabil cu capacitatea de
prelucrare a calculatoarelor moderne. Prin urmare, timpul de calcul va fi mai mic dect 1
secund.

17
Numere

Se consider o mulime finit, elementele creia snt numere naturale mai mici ca
32700. Scriei un program, care selecteaz din aceast mulime numerele prime.

Date de intrare.
Fiierul text NUMERE.IN conine pe fiecare linie cte un numr natural.

Date de ieire.
Fiierul text NUMERE.OUT va conine pe fiecare linie cuvntul DA dac numrul natural
din linia respectiv a fiierului de intrare este un numr prim i NU n caz contrar.

Exemplu.

NUMERE.IN NUMERE.OUT

41
4
53
DA
NU
DA

Restricii. Fiierul de intrare NUMERE.IN conine cel mult 100 de linii. Timpul de
execuie nu va depi 0.5 secunde. Fiierul surs va avea denumirea NUMERE.PAS,
NUMERE.C sau NUMERE.CPP.

Rezolvare

Conform definiiei, numrul i, i > 2, este prim dac el se mparte fr rest numai la 1 i
la el nsui. Aceast definiie poate fi aplicat direct mprind numrul i la 2, 3, 4, ..., (i div
2). Evident, timpul cerut de un astfel de algoritm T ~i. Lund n considerare faptul c valoarea
maxim a numrului i este 32700, iar fiierul de intrare conine cel mult 100 de numere,
timpul de execuie T ~32700 100 ~3 10
6
. Intruct capacitatea de prelucrare a calculatorului
personal este de ordinul 10
9
instruciuni pe secund, timpul de execuie T va fi mai mic ca 0,5
secunde.


Program Numere;
{ Clasele 07-09 }
var i : integer;
Finput, Foutput : text;

function EsteNumarPrim(i : integer) : string;
{ Returneaza DA daca numarul i este prim si NU in caz contrar }
label 1;
var j : integer;
begin
EsteNumarPrim:='DA';
if (i=0) or (i=1) then
begin
EsteNumarPrim:='NU';
goto 1;
end;
for j:=2 to (i div 2) do
if (i mod j)=0 then
begin
EsteNumarPrim:='NU';
18
goto 1;
end;
1: end; { EsteNumarPrim }

begin
assign(Finput, 'NUMERE.IN');
assign(Foutput, 'NUMERE.OUT');
reset(Finput);
rewrite(Foutput);
while not eof(Finput) do
begin
readln(Finput, i);
writeln(Foutput, EsteNumarPrim(i));
end;
close(Finput);
close(Foutput);
end.

19
Lupta maritim

n cunoscutul joc Lupta maritim, aciunea are loc pe o foaie de hrtie, liniat n
ptrele, cu dimensiunile m rnduri i n coloane. Pe cmpul de joc se pot afla corbii
distincte de form arbitrar. Fiecare corabie reprezint o figur conex (figura se numete
conex dac din orice ptrel al ei se poate ajunge la oricare alt ptrel din componena
acesteia, deplasarea din ptrelul curent n unul din cele patru ptrele vecine efectundu-se
prin latura comun). Oricare dou corbii nu au puncte comune i nu se ating la coluri (vezi
desenul din exemplu).
n procesul jocului, fiecare din corbii se poate afla n una din urmtoarele trei stri:
- dac n corabie nu s-a nimerit nici o dat, ea se consider vie;
- dac n fiecare din ptrelele din componena unei corabii s-a nimerit, ea se
consider ucis;
- n caz contrar corabia se consider rnit.
Sarcin. Elaborai un program care calculeaz numrul corbiilor vii, numrul
corbiilor ucise i numrul corbiilor rnite.
Date de intrare. Fiierul text LUPTA.IN conine pe o singur linie numerele ntregi m
i n, separate prin spaiu. Fiecare din urmtoarele m linii ale fiierului de intrare conine cte n
numere ntregi, separate prin spaiu. Aceste numere pot lua urmtoarele valori:
- 0 acest ptrel nu aparine nici la o corabie (este ap);
- 1 acest ptrel aparine unei corbii, dar n el nu s-a nimerit;
- -1 acest ptrel aparine unei corbii i n el s-a nimerit.
Date de ieire. Fiierul text LUPTA.OUT va conine pe singur linie trei numere ntregi,
separate prin spaiu: numrul de corbii vii, numrul de corbii ucise i numrul de
corbii rnite.
Exemplu.
LUPTA.IN LUPTA.OUT

7 8
0 0 1 0 -1 -1 0 1
1 0 0 0 0 0 0 0
1 0 1 1 1 1 1 0
-1 0 1 0 0 0 1 0
0 0 1 1 1 -1 1 0
1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0
3 1 2


Restricii. 25 , 1 s s n m . Timpul de execuie nu va depi 0,1 secunde. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
LUPTA.PAS, LUPTA.C sau LUPTA.CPP.

Rezolvare

Din analiza enunului problemei se observ c operaia de baz, ce trebuie efectuat de
mai multe ori, const n determinarea tuturor ptrelelor, care formeaz o corabie. Aceast
operaie poate fi fcut recursiv dup cum urmeaz.
Presupunem c ptrelul curent (x, y) aparine unei corbii. n procesul de analiz a
ptrelului curent, n funcie de valoarea respectiv, vom incrementa unul din contoarele k
1

20
(nu s-a nimerit) sau k
2
(s-a nimerit). Pentru a exclude ptrelul curent din analizele ulterioare,
nscriem n el valoarea 0.
n continuare, vom analiza recursiv ptrelul de sus (x-1, y), cel din dreapta (x, y+1),
cel din stnga (x, y-1) i cel de jos (x+1, y).
Dup parcurgerea tuturor ptrelelor din componena unei corbii, n funcie de valorile
k
1
i k
2
, incrementm unul din contoarele t
1
(vii), t
2
(ucise) sau t
3
(rnite).
Pentru a evita verificrile de la marginile cmpului de joc, n programul ce urmeaz el
este ncadrat n zerouri.

Program LuptaMaritima;
{ Clasele 07-09 }
const
mmax=25; nmax=25;
{ tablouri ce defines coordonatele patratelelor vecine }
dx : array [1..4] of integer = (-1, 1, 0 ,0);
dy : array [1..4] of integer = (0, 0, -1 ,1);
var
A : array [0..mmax+1, 0..nmax+1] of integer; { campul de joc }
m, n : integer; { dimensiunile curente ale campului de joc }
t1, t2, t3 : integer;
{ numarul de corabii: t1 - vii; t2 - ucise; t3 - ranite }
k1, k2 : integer;
{ k1 - numarul patratelelor in care nu s-a nimerit }
{ k2 - numarul patratelilor in care s-a nimerit }

procedure Citeste;
var i, j : integer;
Intrare : text;
begin
assign(Intrare, 'LUPTA.IN');
reset(Intrare);
readln(Intrare, m, n);
{ formam cadrul de zerouri al campului de joc }
for i:=0 to m+1 do A[i, 0]:=0; { marginea stanga }
for j:=0 to n+1 do A[0, j]:=0; { marginea de sus }
for j:=0 to n+1 do A[m+1, j]:=0; { marginea de jos }
for i:=0 to m+1 do A[i, n+1]:=0; { marginea dreapta }
{ citim patratelele din fisierul de intrare }
for i:=1 to m do
begin
for j:=1 to n do
read(Intrare, A[i, j]);
readln(Intrare);
end; { for }
close(Intrare);
end; { Citeste }

procedure Scrie;
var Iesire : text;
begin
assign(Iesire, 'LUPTA.OUT');
rewrite(Iesire);
writeln(Iesire, t1, ' ', t2 , ' ', t3);
close(Iesire);
end; { Scrie }

procedure Corabie(x, y : integer);
{ Exploreaza patratelele unei corabii }
var i : integer;
begin
if (A[x, y] <> 0) then
21
begin
{ patratelul apartine corabiei }
if (A[x,y]=1) then k1:=k1+1
else k2:=k2+1;
A[x, y]:=0;
{ analizam patratelele vecine }
for i:=1 to 4 do
Corabie(x+dx[i], y+dy[i]);
end; { if }
end; { Corabie }

procedure NumaraCorabiile;
{ Numara corabiile de pe campul de joc }
var i, j : integer;
begin
t1:=0; t2:=0; t3:=0;
for i:=1 to m do
begin
for j:=1 to n do
if A[i,j]<>0 then
begin
k1:=0; k2:=0;
Corabie(i, j);
if (k2=0) then t1:=t1+1 else
if (k1=0) then t2:=t2+1 else t3:=t3+1;
end; { if }
end; { for }
end; { NumaraCorabiile }

begin
Citeste;
NumaraCorabiile;
Scrie;
end.

Din analiza textului procedurii NumaraCorabiile se observ c apelul procedurii
Corabie va fi efectuat de cel mult mn ori. n cel mai ru caz, n procedura Corabie vor fi
parcurse toate cel mn ptrele.
Prin urmare, numrul de operaii cerut de program va fi de ordinul
2
) (mn . Evident,
pentru 25 , s n m , numrul respectiv de operaii va fi cu mult mai mic dect capacitatea de
prelucrare a calculatoarelor moderne.

22

Clasele 10 12



Denumirea
problemei
Numrul de
puncte alocat
problemei
Denumirea
fiierului surs
Denumirea
fiierului de
intrare
Denumirea
fiierului de
ieire
Vslaii 100
VASLASI.PAS
VASLASI.C
VASLASI.CPP
VASLASI.IN VASLASI.OUT
Iepuraii 100
IEPURE.PAS
IEPURE.C
IEPURE.CPP
IEPURE.IN IEPURE.OUT
Segmente 100
SEGMENT.PAS
SEGMENT.C
SEGMENT.CPP
SEGMENT.IN SEGMENT.OUT
arpele 100
SARPE.PAS
SARPE.C
SARPE.CPP
SARPE.IN SARPE.OUT
Descompuneri 100
DESC.PAS
DESC.C
DESC.CPP
DESC.IN DESC.OUT
Radioul 100
RADIO.PAS
RADIO.C
RADIO.CPP
RADIO.IN RADIO.OUT
Total 600 - - -

23
Vslaii

n sfrit, n anul 2009, informaticienii au putut rezolva problema, care de mult timp i
chinuia pe vslaii-sportivi.
Se consider o barc lung cu vsle, n care sunt amplasai n vslai. Fiecare vsla are
cte o vsl, cu care el vslete doar pe o singur parte a brcii partea stnga sau partea
dreapta. Evident, vslind, fiecare sportiv comunic brcii o accelerare nu doar strict n direcia
micrii brcii, dar i perpendicular pe ea. Apare ntrebarea, cum trebuie s orientm vslele
sportivilor din barc, pentru ca ea s nu aib oscilaii, perpendiculare pe direcia micrii.



Din punct de vedere matematic, soluia problemei se reduce la amplasarea semnelor +
(vsla pe partea stng) i - (vsla pe partea dreapt) n faa numerelor de vslai 1, 2, 3, ...n
n aa mod, nct suma obinut s fie egal cu zero.
De exemplu, pentru 4 = n , avem 0 4 3 2 1 = + + . Prin urmare, vslaii 1 i 4 vor
vsli pe partea stnga, iar vslaii 2 i 3 pe partea dreapta a brcii.

Sarcin. Elaborai un program care amplaseaz semnele +, - n faa numerelor din
irul 1, 2, 3, ..., n n aa mod, nct suma obinut s fie egal cu zero.
Date de intrare. Fiierul text VASLASI.IN conine pe o singur linie numrul ntreg n.
Date de ieire. Fiierul text VASLASI.OUT va conine pe o singur linie un ir din n
caractere, format din semnele +, -. Dac problema are mai multe soluii, n fiierul de ieire se
va scrie doar una, oricare din ele. Dac problema nu are soluii, n fiierul de ieire se va scrie
un ir din n caractere, format din semnul ?.
Exemplul 1.
VASLASI.IN VASLASI.OUT
4 +--+

Exemplul 2.
VASLASI.IN VASLASI.OUT
5 ?????

Restricii. 21 2 s s n . Timpul de execuie nu va depi 2,0 secunde. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
VASLASI.PAS, VASLASI.C sau VASLASI.CPP.

Rezolvare

Problema poate fi rezolvat prin metoda trierii, examinnd toate variantele posibile de
amplasare a semnelor +, - n faa numerelor din irul 1, 2, 3, ..., n.
Pentru a genera variantele de amplasare, vom folosi numrul binar Orientare, cifra i a
cruia ia valoarea 0 daca vslaul i are vsla pe partea stng a brcii i valoarea 1 n caz
contrar.
24
Iniial, numrul binar Orientare are valoarea 00...000. n continuare, la fiecare iteraie
vom aduna la acest numr valoarea binar 1, operaie cunoscut n informatic ca
incrementare. n programul ce urmeaz, numrul binar Orientare este reprezentat prin tabloul
cu acelai nume, iar operaia de incrementare se realizeaz cu ajutorul procedurii Increment.

Program Vaslasii;
{ Clasele 10-12 }
const nmax=21;

var n : integer; { numarul de vaslasi }
S : string; { raspunsul }
Orientare : array [1..nmax] of integer; {orientarea vaslelor }
Transport : integer;

procedure Citeste;
{ citirea datelor de intrare }
var Intrare : text;
begin
assign(Intrare, 'VASLASI.IN');
reset(Intrare);
readln(Intrare, n);
close(Intrare);
end; { Citeste }

procedure Scrie;
{ srierea datelor de iesire }
var Iesire : text;
begin
assign(Iesire, 'VASLASI.OUT');
rewrite(Iesire);
writeln(Iesire, S);
close(iesire);
end; { Scrie }

procedure Increment;
{ incrementarea numarului binar Orientare }
var i : integer;
begin
Transport:=1;
for i:=1 to n do
begin
Orientare[i]:=Orientare[i]+Transport;
if Orientare[i]<=1 then Transport:=0
else
begin
Orientare[i]:=Orientare[i]-2;
Transport:=1;
end; { else }
end; { for }
end; { Increment }

function Suma : integer;
{ calculeaza suma numerelor ce reprezinta vaslasii, }
{ luand in considerare cifrele binare din Orientare }
var i : integer;
P : integer; { sumele intermediare }
begin
P:=0;
for i:=1 to n do
if Orientare[i]=0 then P:=P+i else P:=P-i;
Suma:=P;
end; { Suma }

25
procedure OrientareaVaslelor;
{ cauta orientarea vaslelor }
label 1;
var i : integer;
begin
{ initial toate vaslele sunt pe partea stanga }
for i:=1 to n do Orientare[i]:=0;
{ trierea tuturor variantelor posibile de orientare }
Transport:=0;
while Transport=0 do
begin
if Suma=0 then
begin
{ a fost gasita o solutie }
S:='';
for i:=1 to n do
if Orientare[i]=0 then S:=S+'+' else S:=S+'-';
goto 1;
end; { if }
{ urmatoare varianta de orientare a vaslelor }
Increment;
end; { while }
{ daca am ajuns aici, solutii nu exista }
S:='';
for i:=1 to n do S:=S+'?';
1: end; { OrientareaVaslelor }

begin
Citeste;
OrientareaVaslelor;
Scrie;
end.

n cel mai ru caz, cnd problema nu are soluii, vor fi examinate toate cele
n
2 valori
posibile ale numrului binar Orientare. Prin urmare, numrul de operaii, cerut de algoritm, va
fi proporional cu n
n
2 . Conform restriciilor problemei, 21 s n . Prin urmare, numrul de
operaii va fi proporional cu
8 21
10 5 . 0 21 2 ~ , mrime comparabil cu capacitatea de
prelucrare a calculatoarelor personale moderne. Evident, pentru valori 21 s n , timpul de
execuie al programului va fi mai mic dect 2 secunde.

26
Iepuraii

n cartea sa Liber Abaci (Cartea Abacului), editat n anul 1202, marele matematician
Fibonacci (cunoscut i sub numele Leonardo Pisano) a rezolvat problema nmulirii
iepurailor, care const n calcularea numrului de iepurai ntr-o populaie ce evolueaz
pornind de la o pereche iniial. Conform acestei cri, dezvoltarea populaiei de iepurai se
descrie cu ajutorul irului 1, 1, 2, 3, 5, 8, 13, ... , denumit irul lui Fibonacci. Formal, acest ir
se definete cu ajutorul urmtoarelor formule:

1
1
= F , 1
2
= F , 2
3
= F , 3
4
= F , ...,
1 1 +
+ =
i i i
F F F , ... ,

unde F
i
reprezint numrul de perechi de iepurai n luna i.

Sarcin. Elaborai un program care calculeaz suma S a primelor n numere din irul lui
Fibonacci.
Date de intrare. Fiierul text IEPURE.IN conine pe o singur linie numrul ntreg n.
Date de ieire. Fiierul text IEPURE.OUT va conine pe singur linie numrul ntreg S.
Exemplu.
IEPURE.IN IEPURE.OUT
4 7

Restricii. 300 2 s s n . Timpul de execuie nu va depi 0,5 secunde. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
IEPURE.PAS, IEPURE.C sau IEPURE.CPP.

Rezolvare

n general, problema nu prezint dificulti de algoritmizare, ntruct suma S poate fi
calculat printr-o singur parcurgere a irului lui Fibonacci. Mai mult ca att, nu este necesar
nici memorarea ntregului ir, ntruct numrul curent poate fi calculat prin nsumarea doar a
celor dou numere precedente din ir.
Prin F1, F2 i F3 vom nota oricare trei numere consecutive din irul lui Fibonacci.
Evident, suma S poate fi calculat cu ajutorul urmtoarei secvene de algoritm:

F1:=1;
F2:=1;
S:=F1;
i:=2;
repeat
S:=S+F2;
F3:=F1+F2;
F1:=F2;
F2:=F3;
i:=i+1;
until i>n;

Implementnd aceast secven cu ajutorul declaraiilor de variabile

var S, F1, F2, F3 : integer;

27
prin experimente de calcul ne convingem, c ncepnd cu 20 > n , la calcularea valorilor
variabilelor n studiu au loc erori de depire.

Cei care cunosc alte tipuri de date, ar putea s ncerce declaraia

var S, F1, F2, F3 : longint;

din Turbo Pascal sau tipurile de date Longword, Int64, Qword din Object Pascal. ns i n
astfel de cazuri, pentru valorile mari ale lui n apar erori de depire.
Pentru a evita erorile de depire, vom reprezenta numerele mari S, F1, F2 i F3 prin
tablouri, formate din K elemente. Fiecare element al tabloului memoreaz cte o cifra a
numrului respectiv.






Dac n procesul adunrii a dou numere mari A i B, formate din cte K cifre,
transportul calculat n cazul nsumrii cifrelor A[1], B[1] i transportului din rangul precedent
difer de zero, are loc o depire i programul va semnaliza o eroare.
n programul ce urmeaz, pentru a realiza experimentul de calcul, n componena
procedurii Adunare a fost inclus urmtoarea secven de instruciuni:

if Transport=1 then
begin
writeln('Depasire');
readln;
end;

Evident, n condiiile problemei, aceast secven de instruciuni nu va fi executat nici
o dat numai atunci, cnd valoarea lui K va fi stabilit suficient de mare. Aceast valoare
poate fi determinat cu ajutorul experimentelor de calcul, atribuindu-i lui n valoarea limit
300 = n , aa cum este indicat n restriciile problemei.

Program Iepurasii;
{ Clasele 10-12 }
const K = 70; { numarul de cifre ale numerelor foarte mari }
type Cifra = 0..9;
Numar = array[1..K] of Cifra;
var n : integer;
S : Numar;

procedure Citeste;
{ Citeste numarul n din fisierul de intrare }
var Intrare : text;
begin
assign(Intrare, 'IEPURE.IN');
reset(Intrare);
readln(Intrare, n);
close(Intrare);
end; { Citeste }

procedure Scrie;
{ Scrie suma S in fisierul de iesire }
var Iesire : text;
j : integer;
1 2 3 ... K
28
begin
assign(Iesire, 'IEPURE.OUT');
rewrite(Iesire);
j:=1;
while S[j]=0 do j:=j+1;
while j<=K do
begin
write(Iesire, S[j]);
j:=j+1;
end; { while }
writeln(Iesire);
close(Iesire);
end; { Scrie }

procedure Unu(var A : Numar);
{ Atribuie numarului A valoarea 1 }
var j : integer;
begin
for j:=1 to K-1 do A[j]:=0;
A[K]:=1;
end; { Unu }

procedure Adunare(A, B : Numar; var C : Numar);
{ Calculeaza suma C:=A+B }
var Transport : Cifra;
j : integer;
q : 0..19;
begin
Transport:=0;
for j:=K downto 1 do
begin
q:=A[j]+B[j]+Transport;
if q<=9 then Transport:=0
else
begin
Transport:=1;
q:=q-10;
end;
C[j]:=q;
end; { for }
if Transport=1 then
begin
writeln('Depasire');
readln;
end;
end; { Adunare }

procedure Suma;
var i : integer;
F1, F2, F3 : Numar;
begin
Unu(F1);
Unu(F2);
S:=F1;
i:=2;
repeat
Adunare(F2, S, S);
Adunare(F1, F2, F3);
F1:=F2;
F2:=F3;
i:=i+1;
until i>n;
end; { Suma }

29
begin
Citeste;
Suma;
Scrie;
end.

ntruct suma S se calculeaz printr-o singur parcurgere a irului lui Fibonacci,
complexitatea temporal a programului este ) (nK O . Conform restriciilor problemei,
300 s n , iar 70 = K . Evident, pentru astfel de valori, timpul de execuie al programului va fi
cu mult mai mic dect 0,5 secunde.

30
Segmente

Se consider mulimea S, format din n segmente distincte, notate prin S
1
, S
2
, ..., S
i
, ...,
S
n
. Fiecare segment S
i
este definit prin coordonatele carteziene ) , (
i i
y x ' ' , ) , (
i i
y x ' ' ' ' ale
extremitilor sale.
Segmentele S
i
, S
j
se numesc
adiacente, dac ele au o extremitate
comun.
Segmentele S
i
, S
j
se numesc
conectate dac exist o succesiune
de segmente, care ncepe cu S
i
i se
termin cu S
j
i n care oricare dou
segmente consecutive sunt
adiacente.
De exemplu, segmentele S
1
, S
5

de pe figura alturat sunt conectate
ntruct exist o succesiune de
segmente consecutiv adiacente: S
1
,
S
3
, S
5
.
Submulimea de segmente Q, Q _ S, formeaz o reea, dac segmentele respective sunt
conectate ntre ele, fr a avea ns conexiuni cu segmentele din submulimea S \ Q.
n general, mulimea de segmente S poate avea mai multe reele.
De exemplu, mulimea de segmente de pe figura de mai sus conine 3 reele: {S
1
, S
2
, S
3
,
S
5
}, {S
4
} i {S
6
, S
7
}.
Sarcin. Elaborai un program, care, cunoscnd mulimea de segmente S, calculeaz
numrul de reele k.
Date de intrare. Fiierul text SEGMENT.IN conine pe prima linie numrul ntreg n.
Fiecare din urmtoarele n linii ale fiierului de intrare conine numerele reale
i i i i
y x y x ' ' ' ' ' ' , , , ,
separate prin spaiu. Linia 1 + i a fiierului de intrare conine coordonatele extremitilor
segmentului S
i
.
Date de ieire. Fiierul text SEGMENT.OUT va conine pe singur linie numrul ntreg k.
Exemplu.
SEGMENT.IN SEGMENT.OUT
7
10.15 0.7 40.15 0.8
20.15 0.6 40.15 0.8
40.15 0.8 70.15 0.4
20.15 0.4 90.15 0.7
50.15 0.2 70.15 0.4
80.15 0.3 11.15 0.8
80.15 0.3 13.15 0.1
3


Restricii. 000 300 3 s s n ; 000 10 , , , 000 10 s ' ' ' ' ' ' s
i i i i
y x y x . Numerele reale din
fiierul de intrare vor conin cel mult trei cifre semnificative dup punctul zecimal. Timpul de
execuie nu va depi 1,5 secunde. Programul va folosi cel mult 32 Megaoctei de memorie
operativ. Fiierul surs va avea denumirea SEGMENT.PAS, SEGMENT.C sau SEGMENT.CPP.

31
Rezolvare
Pentru nceput, vom ncerca s construim consecutiv fiecare din eventualele reele:
1. Iniial, stabilim } { :
1
S Q = i } { \ :
1
S S S = .
2. Examinnd fiecare segment din mulimea S, le selectm pe cele conectate cu
segmentele din Q i le trecem din S n Q. Dac astfel de segmente nu mai exist,
s-a obinut o reea.
3. n continuare, stabilim } { :
i
S Q = , unde S
i
este unul din segmentele rmase n
mulimea S i construim din nou o eventual reea.
4. Procesul de construire a reelelor se termin atunci, cnd mulimea S devine una
vid.
Dei complexitatea unui astfel de algoritm este de ordinul
2
n , la implementarea lui ntr-
un limbaj de programare de nivel nalt va trebui s inem cont de faptul, c parcurgerea
elementelor unei mulimi mai necesit nc cel puin n operaii. Prin urmare, complexitatea
temporal a programului respectiv va fi de ordinul
3
n .
Conform restriciilor problemei, 000 300 3 s s n . n consecin, numrul de operaii
cerut de program va fi de ordinul
14
10 , mrime ce depete capacitatea de prelucrare a
calculatoarelor personale instalate n laboratoarele de informatic. Evident, timpul de execuie
a unui astfel de program va fi de ordinul
3
10 secunde.
Pentru a reduce complexitatea programului, vom implementa un algoritm ce conine
dou etape:
Etapa I preprocesarea datelor, fapt ce va facilita cutarea perechile de segmente
adiacente.
Etapa II construirea reelelor propriu-zise. ns, spre deosebire de algoritmul
precedent, n care reelele erau construite consecutiv, una dup alta, n algoritmul propus
reelele respective sunt construite concomitent, informaia despre fiecare din ele fiind stocat
ntr-o structur arborescent de date.
n programul de mai jos, la prima etap, se creeaz structura de date TPuncte. Aceast
structur, conine informaiile complete ) , , , , ( i y x y x
i i i i
' ' ' ' ' ' despre fiecare segment, ordonate n
ordinea creterii coordonatelor x (primul criteriu de sortare) i y (al doilea criteriu de sortare).
ntruct dup o astfel de sortare segmentele adiacente vor fi plasate pe poziii vecine, gsirea
segmentelor adiacente necesit doar o singur parcurgere a tabloului TPuncte.
La etapa a doua sunt construite reelele propriu-zise. Informaia despre reelele
respective se stocheaz ntr-un set de arbori inversai (rooted tree forest), care cresc de jos
n sus.
Fiecare nod al unui astfel de arbore conine cmpurile tata i sz. Cmpul tata conine
identificatorul segmentului deja inclus n reea, iar cmpul sz conine numrul total de noduri
aflat in arbore sub nodul curent.
Iniial, nodul care e singur (izolat), va avea n cmpul tata valoarea -1 (nu are tata), iar
n cmpul sz valoarea 1. n procesul execuiei procedurii AflaComponenta(a), algoritmul
urca de-a lungul tailor pana la nodul din vrf, care nu are tata. Identificatorul acelui
nod i va fi identificatorul reelei din care face parte segmentul a.
n procesul execuiei procedurii Uneste(a, b), la nceput se identifica componenta lui
a si componenta lui b. In cazul in care acestea sunt identice, nu se face nimic. n cazul n care
componentele sunt diferite, se ataeaz componenta mai mica la cea mai mare, astfel nct
arborele sa aib adncimea egala cu cel mult n log .

32
Program Segmente;
{ Clasele 10-12 }
const MAXN = 320000;
type TPunct = record
x, y: longint;
segment: longint;
end;
type TPuncte = array[1..2 * MAXN] of TPunct;
var capete: TPuncte; {punctele}
var n: longint; {numarul de segmente}

procedure Citeste;
{ citeste fisierul de intrare si construieste tabloul cu puncte }
var f: text;
x1, y1, x2, y2: double;
i: longint;
begin
assign(f, 'SEGMENTE.IN');
reset(f);
readln(f, n);
for i := 1 to n do
begin
readln(f, x1, y1, x2, y2);
capete[2 * i - 1].x := round(x1 * 1000);
capete[2 * i - 1].y := round(y1 * 1000);
capete[2 * i - 1].segment := i;
capete[2 * i].x := round(x2 * 1000);
capete[2 * i].y := round(y2 * 1000);
capete[2 * i].segment := i;
end;
close(f);
end; { Citeste }

function ComparaPuncte(var a, b : TPunct) : integer;
{ Compara doua puncte }
{ returneaza -1 daca a e "mai mic" ca b, +1 daca e "mai mare", }
{ zero daca sunt "egale" }
begin
if a.x < b.x then exit(-1);
if a.x > b.x then exit(1);
{a.x = b.x}
if a.y < b.y then exit(-1);
if a.y > b.y then exit(1);
exit(0);
end; { ComparaPuncte }

procedure Qsort(var data : TPuncte; a, b : longint);
{ Sortarea rapida a subsirul a..b al sirului de puncte data }
var left, right : longint;
aux, pivot : TPunct;
begin
pivot := data[(a + b) div 2];
left := a;
right := b;
while left <= right do
begin
while ComparaPuncte(data[left], pivot) < 0 do left := left + 1;
while ComparaPuncte(data[right], pivot) > 0 do right := right - 1;
if left <= right then
begin
{ schimbam cu locul Data[left] si Data[right] }
aux := data[left];
data[left] := data[right];
33
data[right] := aux;
left := left + 1;
right:= right - 1;
end;
end;
if right > a then qsort(data, a, right); { sortam subsirul STANGA }
if b > left then qsort(data, left, b); { sortam subsirul DREAPTA }
end; { Qsort }

var i: longint;

{ Nod - elementul de baza al Rooted Tree Forest }
type TNod = record
tata: longint;
sz: longint;
end;

{ setul de arbori inversati: Rooted Tree Forest }
var retele: array[1.. 2 * MAXN] of TNod;

function AflaComponenta(a : longint): longint;
{ Returneaza identificatorul retelei in care se afla elementul a }
var pozitie, z, nextPos : longint;
begin
z := 1;
pozitie := a;
while retele[pozitie].tata > 0 do pozitie := retele[pozitie].tata;
z := pozitie; {salvam id-ul retelei in z}
pozitie := a;
while pozitie <> z do
{ parcurgem arborele "de jos in sus" }
begin
nextPos := retele[pozitie].tata;
retele[pozitie].tata := z;
pozitie := nextPos;
end;
AflaComponenta := z;
end; { AflaComponenta }

procedure Uneste(a, b : longint);
{ Proceseaza informatia ca a si b sunt in aceeasi componenta }
var componentaA, componentaB : longint;
begin
componentaA := AflaComponenta(a);
componentaB := AflaComponenta(b);
if componentaA = componentaB then exit;
{ nu e nimic de facut, componentele sunt deja unite }
if retele[componentaA].sz < retele[componentaB].sz then
begin
{ componenta A e mai mica decat B, deci punem pe A sub B }
retele[componentaA].tata := componentaB;
inc(retele[componentaB].sz, retele[componentaA].sz);
end
else
begin {invers; componenta B e mai mica decat A, deci punem pe B sub A}
retele[componentaB].tata := componentaA;
inc(retele[componentaA].sz, retele[componentaB].sz);
end;
end; { Uneste }

var capDeRetea : array[1..MAXN] of boolean;
rezultat, componenta : longint;
f : text;
34

begin { programul principal }
{ Etapa I }
Citeste;
Qsort(capete, 1, 2 * n);
{ Etapa II }
for i := 1 to 2 * n do
{ initializam setul de arbori: fiecare segment e singur, }
{ neconectat cu nimeni }
begin
retele[i].tata := -1; { nu are tata, deci e singur }
retele[i].sz := 1; { un singur element in set - segmentul insusi }
end;
for i := 2 to 2 * n do
{ procesam informatia despre conexiunile dintre segmente }
begin
if ComparaPuncte(capete[i-1], capete[i]) = 0 then
Uneste(capete[i-1].segment, capete[i].segment);
end;
fillchar(capDeRetea, sizeof(capDeRetea), false);
for i := 1 to n do
begin
componenta := AflaComponenta(i);
capDeRetea[componenta] := true;
end;
rezultat := 0;
for i := 1 to n do
if capDeRetea[i] then inc(rezultat);
assign(f, 'segmente.out');
rewrite(f);
writeln(f, rezultat);
close(f);
end.

Din analiza operaiilor efectuate n cadrul Etapelor I i II rezult, c complexitatea
temporal a programului este ) log ( n n O . Conform restriciilor problemei, 000 300 3 s s n .
Prin urmare, numrul de operaii va fi de ordinul
7
10 , mrime comparabil cu capacitatea de
prelucrare a calculatoarelor personale din laboratorul de informatic.
Menionm, c numerele reale ce reprezint coordonatele extremitilor de segmente
conin cel mult trei cifre dup virgul. Putem folosi acest fapt pentru a trece de la
coordonatele reale la cele ntregi, care, evident, sunt procesate mult mai rapid. Anume din
aceste considerente, n procedura Citeste, numerele reale, citite din fiierul de intrare, sunt
nmulite cu 000 1 i transformate n numere ntregi.

35
arpele

Intr-o versiune simplificat a binecunoscutul joc clasic de calculator Snake (arpele), un
arpe trebuie sa mnnce toate merele dintr-o livada. Regulile acestui joc sunt foarte simple:
1. n livada sunt mprtiate pe pmnt n mere, poziia fiecrui mar i fiind definit prin
coordonatele carteziene ntregi (x
i
, y
i
).
2. Originea sistemului de coordonate se afl n colul stnga-jos al livezii. Axa de
coordonate 0X este orientat de la stnga la dreapta, iar axa 0Y de jos n sus.
3. La nceputul jocului, n livada intra un arpe, care are drept scop s mnnce toate
merele din livada.
4. Poziia curent a arpelui este definit prin coordonatele carteziene ntregi (x
s
, y
s
).
5. Poziia iniial a arpelui este (1, 1).
6. Repertoriul de comenzi ale arpelui include instruciunile de deplasare SUS, JOS,
STNGA, DREAPTA. Execuia unei astfel de comenzi const n deplasarea arpelui
n direcia respectiv exact cu o unitate de lungime.
7. Atunci cnd coordonatele arpelui devin egale cu coordonatele unui mr, arpele
mnnc mrul respectiv.
Scopul jocului const n deplasarea arpelui n aa mod, nct toate merele s fie
mncate, iar drumul parcurs de arpe s fie ct mai scurt.
Dorin este mptimit de jocul Snake. El l joaca de mult vreme i a stabilit mai multe
recorduri. Din pcate, pe consola lui de jocuri s-a defectat butonul comenzii JOS. n
consecin, arpele, indiferent de poziia n care se afla, poate executa doar comenzile SUS,
STNGA, DREAPTA.
Dorin ins nu s-a descurajat i consider aceast defeciune o nou provocare. ntruct
Dorin a devenit foarte priceput la acest joc, el dorete s stabileasc un nou record. El nu se
ndoiete deloc de abilitile lui de a direciona arpele pe orice drum posibil, dar vrea s tie
lungimea celui mai scurt drum, deplasndu-se pe care arpele ar mnca toate merele.
Sarcin. Elaborai un program, care, cunoscnd coordonatele celor n mere, calculeaz
lungimea L a celui mai scurt drum, deplasndu-se pe care arpele ar mnca toate merele.
Date de intrare. Fiierul text SARPE.IN conine pe prima linie numrul ntreg n.
Fiecare din urmtoarele n linii ale fiierului de intrare conine numerele ntregi x
i
, y
i
, separate
prin spaiu. Linia 1 + i a fiierului de intrare conine coordonatele mrului i.
Date de ieire. Fiierul text SARPE.OUT va conine pe singur linie numrul ntreg L.
Exemplu.
SARPE.IN SARPE.OUT
5
2 2
5 3
7 3
8 4
4 6
16


Restricii. 000 10 1 s s n ; 000 10 , 1 s s
i i
y x . Timpul de execuie nu va depi 0,1
secunde. Programul va folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va
avea denumirea SARPE.PAS, SARPE.C sau SARPE.CPP.
36

Rezolvare
Pentru a reduce numrul de cazuri particulare, vom include n mulimea merelor nc
unul, aflat pe poziia (1, 1).
n continuare, introducem n studiu un set de m linii drepte, paralele cu axa de
coordonate X, pe fiecare din care se afl cel puin cte un mr. Evident, fiecare din aceste linii
poate fi definit univoc prin coordonata y
p
, } ..., , , {
2 1 m p
y y y y e . ntruct avem garantat un
mr pe poziia (1, 1), rezult c 1
1
= y .
Prin
p
x' vom nota abcisa celui mai din stnga, iar prin
p
x ' ' abcisa celui mai din dreapta
mr de pe linia y
p
.
Deoarece repertoriul de comenzi ale
arpelui nu mai conine comanda JOS, odat
ajuns pe o linie cu mere, arpele trebuie s
mnnce toate merele de pe aceast linie i
abia apoi s se deplaseze n sus.
n aceste condiii, lungimea celui mai
scurt drum poate fi calculat prin metoda
programrii dinamice, pornind de la linia cea
de mai jos i terminnd cu linia cea de mai
sus. Pentru fiecare linie, arpele poate termina
parcurgerea ei n una din dou poziii: la
captul stng sau la captul drept al liniei.
Pentru a formaliza acest proces, introducem n studiu urmtoarele funcii:
) (
p
y L' lungimea celui mai scurt drum, parcurgndu-l pe care arpele mnnc toate
merele de pe liniile de mai jos i de pe linia curent, rmnnd n poziia celui mai din stnga
mr de pe linia curent y
p
;
) (
p
y L' ' lungimea celui mai scurt drum, parcurgndu-l pe care arpele mnnc toate
merele de pe liniile de mai jos i de pe linia curent, rmnnd n poziia celui mai din dreapta
mr de pe linia curent y
p
.
Valorile acestor funcii pot fi calculate dup urmtoarele formule recurente:

1 * 2 ) 1 ( ' ' ' = '
p p
x x L ; 1 ) 1 ( ' ' = ' '
p
x L ;
) ( |] | ) ( |, | ) ( min[ ) ( ) (
1 1 1 1 1 p p p p p p p p p p p
x x x x y L x x y L y y y L ' ' ' + ' ' ' ' + ' ' ' ' ' + ' + = '

;
) ( |] | ) ( |, | ) ( min[ ) ( ) (
1 1 1 1 1 p p p p p p p p p p p
x x x x y L x x y L y y y L ' ' ' + ' ' ' + ' ' ' ' + ' + = ' '

.

Evident, lungimea celui mai scurt drum va fi:

)] ( ), ( min[
m m
y L y L L ' ' ' = .

unde y
m
este coordonata celei mai de sus linii.

Program Sarpele;
{ Clasele 10-12 }
const MAXPOZ = 10000;
var n:longint;
px, py, num, minx, maxx, costMinX, costMaxX
:array[1..MAXPOZ] of longint;
37
costMin:longint;

procedure Citeste;
var fin:text;
i:longint;
begin
assign(Intrare, 'SARPE.IN');
reset(Intrare);
readln(Intrare, n);
for i := 1 to n do
readln(Intrare, px[i], py[i]);
close(Intrare);
end; { Citeste }

procedure GruparePeLinii;
var i,x,y:longint;
begin
num[1] := 1;
minx[1] := 1;
maxx[1] := 1;
for i := 1 to n do
begin
x := px[i];
y := py[i];
inc(num[y]);
if (num[y] = 1) then
begin
minx[y] := x;
maxx[y] := x;
end
else
begin
if (x < minx[y]) then minx[y] := x;
if (maxx[y] < x) then maxx[y] := x;
end;
end;
end; { GruparePeLinii }

function min(a, b: longint):longint;
begin
if (a < b) then min := a
else min := b;
end;

procedure CalculCosturiMin;
var i, prevY, y, dy :longint;
var costMinInMin, costMaxInMin, costMinInMax, costMaxInMax: longint;
begin
costMinX[1] := 2 * maxx[1] - minx[1] - 1;
costMaxX[1] := maxx[1] - 1;

prevY := 1;
for i := 2 to MAXPOZ do
if (num[i] > 0) then
begin
y := i;
dy := y - prevY;
costMinInMin := costMinX[prevY] + abs(maxx[y]-minx[prevY]);
costMaxInMin := costMaxX[prevY] + abs(maxx[y]-maxx[prevY]);
costMinInMax := costMinX[prevY] + abs(minx[y]-minx[prevY]);
costMaxInMax := costMaxX[prevY] + abs(minx[y]-maxx[prevY]);
costMinX[y] := min(costMinInMin, costMaxInMin)
+ dy + (maxx[y] - minx[y]);
costMaxX[y] := min(costMinInMax, costMaxInMax)
38
+ dy + (maxx[y] - minx[y]);
prevY := y;
end;
costMin := min(costMinX[prevY], costMaxX[prevY]);
end; { CalculCosturiMin }

procedure Scrie;
var fout: text;
begin
assign(Iesire, 'SARPE.OUT');
rewrite(Iesire);
writeln(Iesire, costMin);
close(Iesire);
end;

begin
Citeste;
GruparePeLinii;
CalculCosturiMin;
Scrie;
end.

Dina analiza textului procedurii GruparePeLinii se observ c pentru determinarea
coordonatelor y
p
,
p
x' i
p
x ' ' este suficient o singur parcurgere liniar a datelor de intrare,
deci complexitatea acestei proceduri este ) (n O . n procedura CalculCosturiMin, valorile
) (
p
y L' i ) (
p
y L' ' se calculeaz de m ori. ntruct n ms , complexitatea acestei proceduri va fi,
de asemenea, ) (n O . Conform restriciilor din enunul problemei, 000 10 s n . Prin urmare,
numrul de operaii cerut de algoritm va fi de ordinul 10
4
, mrime cu mult mai mic dect
capacitatea de prelucrare a calculatoarelor din laboratorul de informatic.


39
Descompuneri

Numim k-descompunere a numrului natural nenul N reprezentarea acestuia ca o sum
de exact k numere naturale nenule, scrise n ordine strict cresctoare. n general, n
dependen de valorile N i k, numrul N poate s nu aib nici una, s aib doar una sau s
aib mai multe k-descompuneri.
Exemple:
- 9 = 9 este o 1-descompunere a numrului 9;
- 9 = 4 + 5 este o 2-descompunere a numrului 9;
- 9 = 3 + 6 este o alt 2-descompunere a numrului 9;
- 9 = 1 + 3 + 5 este o 3-descompunere a numrului 9;
- 9 = 1 + 2 + 6 este o alt 3-descompunere a numrului 9.
Contraexemple:
- 9 = 3 + 1 + 5 nu este o 3-descompunere a numrului 9, ntruct n scrierea respectiv
termenii 3 i 1 nu apar n ordine cresctoare;
- 9 = 1 + 1 + 7 nu este o 3-descompunere a numrului 9, ntruct n scrierea respectiv
termenii 1 i 1 nu apar n ordine strict cresctoare;
- 9 = 0 + 9 nu este o 2-descompunere a numrului 9, ntruct n scrierea respectiv
apare numrul 0.
Sarcin. Elaborai un program, care, cunoscnd numerele naturale N i k, calculeaz
numrul T de k-descompuneri posibile ale lui N. Deoarece numrul T poate fi foarte mare, n
fiierul de ieire se va scrie doar restul mpririi lui T la 1000000000 (un miliard).
Date de intrare. Fiierul text DESC.IN conine pe singura linie numerele ntregi N i k,
separate prin spaiu.
Date de ieire. Fiierul text DESC.OUT va conine pe singur linie restul mpririi
numrului ntreg T la 1000000000 (un miliard).
Exemple:
1) DESC.IN DESC.OUT
12 3 7

2) DESC.IN DESC.OUT
90 1 1

3) DESC.IN DESC.OUT
300 9 382665027

Restricii. 700 1 s s s N k . Timpul de execuie nu va depi 0,5 secunde. Programul va
folosi cel mult 32 Megaoctei de memorie operativ. Fiierul surs va avea denumirea
DESC.PAS, DESC.C sau DESC.CPP.

Rezolvare

Problema poate fi rezolvat prin metoda programrii dinamice. n acest scop,
introducem n studiu funcia ) , , ( b a k t . Aceast funcie reprezint numrul de k-descompuneri
ale numrului a, care au ca termen minim pe b.
Evident, 1 ) , , 1 ( = b a t dac b a = i 0 ) , , 1 ( = b a t n caz contrar. n general, se poate
demonstra c funcia n studiu poate fi scris ntr-o form recurent:
40

+ =
=
b a
b i
i b a k t b a k t
1
) , , 1 ( ) , , ( .

Prin urmare, numrul T poate fi calculat prin nsumarea valorilor ) , , ( j N k t pentru
N j ..., , 2 , 1 = :


=

+ = =
= =
N
j
j N
j i
N
j
i j N k t j N k t T
1 1 1
) , , 1 ( ) , , ( .

Complexitatea temporal a unui algoritm bazat pe formula recurent de mai sus este
) (
3
kN O . n cazul restriciilor problemei, numrul de operaii cerut de algoritm va fi de
ordinul
14
10 , mrime cu mult mai mare dect capacitatea de prelucrare a calculatoarelor
personale din laboratorul de informatic.
Pentru a reduce complexitatea temporal a algoritmului vom lua n considerare faptul c
pentru valorile lui b a i > funcia 0 ) , , ( = b a k t . n consecin, vom calcula doar sumele din
dreapta indicelui respectiv. n acest caz complexitatea algoritmului va fi ) (
2
N N O , iar
numrul cerut de operaii va fi de ordinul
8
10 , mrime comparabil cu capacitatea de
prelucrare a calculatoarelor din laboratorul de informatica.
Menionm faptul, c n procesul de efectuare a calculelor nu se cere memorarea tuturor
valorilor ) , , ( b a k t , fiind suficiente doar valorile iteraiei curente i termenii din dreapta, n
total circa 4 Megaoctei de memorie operativ.

Program Descompuneri;
{ Clasele 10-12 }
const MAXN = 1000;
const MODULO = 1000 * 1000 * 1000;
type TMatrice = array[1..MAXN, 1..MAXN] of longint;
var f: text;
n, m: longint;
a: TMatrice;
sumaLaDreapta: TMatrice;
i, j, k: longint;
res: longint;
existaElementNenul: boolean;

procedure scrieRezultat(q: longint);
{ Scrie rezultatul n fiierul de ieire i termin programul }
var f: text;
begin
assign(f, 'DESC.OUT');
rewrite(f);
writeln(f, res);
close(f);
halt(0);
end; { Scrie }

begin
fillchar(a, sizeof(a), 0);
fillchar(sumaLaDreapta, sizeof(sumaLaDreapta), 0);
assign(f, 'DESC.IN');
reset(f);
readln(f, n, m);
41
close(f);
{ construim cazul de baza: t[1, x, y] }
for j := 1 to n do
for k := 1 to n do
begin
if (j = k) then a[j, k] := 1 else a[j, k] := 0;
end;
{ partea principal: calcularea t[k, x, y] pentru k > 1 }
for i := 2 to m do
begin
{ calculm sumaLaDreapta }
for j := 1 to n do
for k := j downto 1 do
sumaLaDreapta[j, k] := (a[j, k] + sumaLaDreapta[j, k + 1]) mod
MODULO;
{ calculm t[i, j, k], care e memorat n a[j, k] }
existaElementNenul := false; {detector de valori nenule in a}
for j := 1 to n do
for k := 1 to n do
begin
a[j, k] := sumaLaDreapta[j - k, k + 1];
if a[j, k] > 0 then existaElementNenul := true;
end;
{ in cazul in care existaElementNenul e false, toate elementele lui a
}
{ calculate ulterior vor fi nule. Prin urmare, terminam calculele }
if not existaElementNenul then scrieRezultat(0);
end;

{ calculam raspunsul }
res := 0;
for k := 1 to n do
begin
inc(res, a[n, k]);
res := res mod MODULO;
end;
scrieRezultat(res);
end.
42
Radioul

Postul de radio Bitul Liber are la dispoziie n emitoare, amplasate n n localiti
distincte. Poziia fiecrui emitor i, n i ..., , 3 , 2 , 1 = , este definit prin coordonatele carteziene
ntregi ) , (
i i
y x .
Fiecare emitor are una i aceiai putere de emisie R. Evident, cu ct emitorul este
mai puternic, cu att este mai mare i distana la care se propag undele emise de el. n
scopuri didactice, se consider, c undele unui emitor de puterea R se propaga pn la
distana de R kilometri.
Dei emitoarele au una i aceiai putere R, fiecare din ele poate fi acordat n mod
individual s emit pe una din frecvenele disponibile frecvena F
1
sau frecvena F
2
.
Este cunoscut faptul, c undele radio interfereaz i, n consecin, dac un receptor este
supus aciunii concomitente a dou unde de aceiai frecven, calitatea recepiei scade brusc.
n general, conform regulilor de radiodifuziune, calitatea recepiei se consider bun doar
atunci, cnd nu exist nici un sector de arie nenul, pe care ajung unde radio, emise de dou
emitoare ce au aceiai frecven de emisie.
Este evident faptul, c pentru a extinde aria pe care pot fi recepionate emisiunile
postului de radio Bitul Liber, puterea de emisie R trebuie mrit. Concomitent, frecvenele
de emisie F
1
, F
2
pentru fiecare din emitoare trebuie alese n aa mod, nct s se asigure
calitatea recepiei emisiunilor respective.
Sarcin. Scriei un program, care, cunoscnd coordonatele emitoarelor, calculeaz
puterea maximal de emisie R, ce mai permite nc alegerea pentru fiecare din emitoare a
unor astfel de frecvene, nct se asigur calitatea recepiei.
Date de intrare. Fiierul text RADIO.IN conine pe prima linie numrul ntreg n.
Fiecare din urmtoarele n linii ale fiierului de intrare conine numerele ntregi x
i
, y
i
, separate
prin spaiu. Linia 1 + i a fiierului de intrare conine coordonatele emitorului i.
Date de ieire. Fiierul text RADIO.OUT va conine pe prima linie numrul real R, scris
cu o precizie nu mai mic de 10
-8
. Linia a doua a fiierului de ieire va conine n numere
ntregi, separate prin spaiu. Numrul care apare pe locul i de pe aceast linie va fi egal cu 1,
dac emitorul i trebuie s emit pe frecvena F
1
, i egal cu 2, dac emitorul respectiv
trebuie s emit pe frecvena F
2
. Dac problema admite mai multe soluii, n fiierul de ieire
se va scrie doar una din ele.
Exemplu.


Restricii. 200 1 3 s s n ;
4 4
10 , 10 s s
i
y x . Timpul de execuie nu va depi 2,0
secunde. Programul va folosi cel mult 64 Megaoctei de memorie operativ Fiierul surs va
avea denumirea RADIO.PAS, RADIO.C sau RADIO.CPP.

Rezolvare

Din enunul problemei rezult c puterea maximal nu poate depi jumtate din
distana dintre emitoarele aflate la cea mai mare distan unul de altul. Prin urmare, valoarea
RADIO.IN RADIO.OUT
4
0 0
0 1
1 0
1 1
0.70710678118654752
1 2 2 1

43
concret a lui R trebuie cutat n intervalul ] , 0 [
max
L , unde
max
L poate fi calculat conform
unor formule evidente:

) ..., , , min(
2 1 min n
x x x x =
;
) ..., , , min(
2 1 min n
y y y y =
;
) ..., , , max(
2 1 max n
x x x x =
;
) ..., , , max(
2 1 max n
y y y y =
.
1
2
) ( ) (
2
min max
2
min max
max
+
+
=
y y x x
L .

Presupunem, c avem la dispoziie o funcie ) (R Test , care ia valoarea 1, dac pentru
puterea de emisie R putem stabili frecvenele emitoarelor n aa mod, nct s garantm
calitatea recepiei, i 0 n caz contrar. Evident, avnd o astfel de funcie, putem calcula puterea
maximal prin metoda njumtirii, pornind de la intervalul iniial ] , 0 [
max
L i 2 /
max
L R = .
Pentru a construi funcia ) (R Test , reunim cu cte o linie emitoarele, distana dintre
care este strict mai mic de R 2 . Evident, oricare dou emitoare, ce sunt reunite printr-o
astfel de linie, trebuie s aib frecvene de emisie diferite.
n continuare, vom ncerca s partiionm mulimea emitoarelor n dou submulimi,
notate prin A i B. n cadrul fiecrei submulimi, emitoarele respective nu trebuie s fie
reunite ntre ele. Dac acest lucru ne reuete, funcia ) (R Test va lua valoarea 1. n caz
contrar, adic a rmas cel puin un emitor, ce nu poate fi incluse nici n submulimea A, nici
n submulimea B, funcia ) (R Test va lua valoarea 0.



1 ) ( = R Test 0 ) ( = R Test


Formarea submulimilor A i B poate fi simulat prin vopsirea fiecrui emitor n una
din cele dou culori: alb sau neagr. Iniial, vopsim un emitor arbitrar n culoare alb sau,
prin alte cuvinte, l includem n submulimea A. n continuare, determinm toate emitoarele,
reunite cu emitorul proaspt vopsit, i le vopsim n culoarea neagr sau, prin alte cuvinte, le
includem n submulimea B. n general, la fiecare pas al algoritmului, ce ncearc vopsirea
vecinilor emitorului curent n culoarea opus. n cazul tehnicii de programare Greedy,
complexitatea unui astfel de algoritm este ) (
2
n O .
44
Evident, dup determinarea valorii maximale R, pentru care funcia ) (R Test mai ea nc
valoarea 1, tuturor emitoarelor din mulimea A li se atribuie una din frecvene, de exemplu,
F
1
, iar celor din submulimea B cealalt frecven, de exemplu, frecvena F
2
.

Program Radio;
{ Clasele 10-12 }
var n : Integer;
var x, y : Array[1..1200] of LongInt;

var Freq : Array [1..1200] of Byte;
{ Frecventa asociata fiecarui emitator radio }

var f : Text;
i, j : Integer;
Left, Right, Med : Longint;
MaxSQDist : Longint;

function SQDist(i,j:Integer) : LongInt;
{ Intoarce patratul distantei intre emitatoarele i si j }
begin
SQDist := (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]);
end;

function Test(R_SQ_4: Longint) : boolean; {parametrul este 4*R*R}
{ verifica daca cu raza de emisie R se poate atribui }
{ frecvente emitatoarelor }
var List : Array [1..1200] of Integer;
var ListSize : Integer; { numarul de noduri in lista }
var i, j, t,curent : Integer;
begin
FillByte(Freq, n, 0);
for i := 1 to n do
begin
if Freq[i]<>0 then continue; { frecventa pentru emitatorul i a fost }
{ aleasa deja }
Freq[i] := 1; {alege frecventa 1 pentru emitatorul i}
{ Initializeaza lista cu un singur element : i }
List[1] := i;
ListSize := 1;
j:=1;
while j <= ListSize do begin
curent := List[j];
for t:=1 to n do
begin
if (t<>curent) and ( SQDist(curent,t) < R_SQ_4 ) then
begin
if (Freq[t] = 0) then
begin
{adauga pe t in lista}
Inc(ListSize);
List[ListSize] := t;
Freq[t] := 3-Freq[curent]; { seteaza frecventa opusa }
end
else
if Freq[t] = Freq[curent] then exit(false);
{ am obtinut o contradictie de frecvente, functia Test }
{ va intoarce imediat false }
end;
end;
Inc(j);
end;
end;
45
Test := true;
end;

begin
{ Citeste datele de intrare }
Assign(f, 'RADIO.IN');
Reset(f);
Readln(f, n);
for i:= 1 to n do Readln(f, x[i], y[i]);
Close(f);

{ Gaseste distanta maxima intre virfuri}
MaxSQDist := 0;
for i:= 1 to n-1 do
for j:= i+1 to n do
if MaxSQDist < SQDist(i, j) then MaxSQDist := SQDist(i, j);
Left := 0;
Right := MaxSQDist + 1;
while Right>Left+1 do
begin
Med := (Left + Right) div 2;
if Test(Med)
then Left:=Med
else Right:=Med;
end;

{ mai apeleaza inca o data pentru a seta Freq[...] }
Test(Left);

{ Scrie raspunsul }
Assign(f,'RADIO.OUT');
Rewrite(f);
writeln(f,(sqrt(Extended(Left))/2):0:18);
for i:=1 to n do Write(f, Freq[i],' ');
writeln(f);
Close(f);
end.