Documente Academic
Documente Profesional
Documente Cultură
al Republici Moldova
Ediţia 2005
Chişinău, 2005
Dragi elevi,
Stimaţi profesori,
În luna martie a anului curent Guvernul Republicii Moldova a aprobat Strategia Naţională de
edificare a societăţii informaţionale „Moldova electronica”.
Societatea informaţională este o formă nouă, mult mai perfectă, a civilizaţiei umane, în care
accesul egal şi universal la informaţie, în corelaţie cu o infrastructură informaţională şi de
comunicaţii dezvoltată, contribuie la o dezvoltare social-economică durabilă, reducerea gradului de
sărăcie, îmbunătăţirea calităţii vieţii, la integrarea în Uniunea Europeană. Practica internaţională
demonstrează impactul pozitiv al infrastructurii informaţionale şi de comunicaţii asupra dezvoltării
societăţii contemporane, care constă în diversificarea posibilităţilor de acces la informaţie şi la
resursele informaţionale publice în toate domeniile de activitate umană: guvernare electronică,
economia electronică, comerţul electronic, învăţământul electronic, cultura electronică, medicina
electronică etc., precum şi în creşterea nivelului de ocupaţie a populaţiei prin crearea a noi locuri de
muncă.
Conform Strategiei Naţionale de edificare a societăţii informaţionale „Moldova electronica”,
document de o importanţă primordială pentru ţara noastră, se preconizează ca pînă în anul 2015
numărul de abonaţi la telefonia fixă şi mobilă va creşte cel puţin de două ori. Pînă la sfîrşitul anului
2006 rata de penetrare a telefoniei fixe va atinge valoarea de 25,0, iar în anii 2005−2007 în zonele
rurale vor fi construite circa 151 de mii de linii telefonice. În scopul asigurării accesului populaţiei
la întreaga gamă de servicii oferite de dezvoltarea tehnologiilor informaţionale, se preconizează
creşterea numărului de calculatoare personale şi al utilizatorilor Internet cu o rată de cel puţin 15%
anual.
Un rol important în edificarea societăţii informaţionale revine educaţiei, în special,
învăţămîntului preuniversitar. Anume în şcoală se formează cultura informaţională şi gîndirea
algoritmică, anume în cabinetele şcolare de informatică învăţăm a face primii paşi în lumea
miraculoasă a algebrei booleene şi sistemelor de numeraţie, limbajelor algoritmice şi tehnicilor de
programare. Succesul acestor paşi depinde în egală măsură atît de dragostea noastră faţă de
informatică, cît şi de perseverenţa cu care învăţăm materiile incluse în programele şcolare.
Desigur, toţi participanţii la Olimpiada Republicană la Informatică au demonstrat de
nenumărate ori aceste calităţi, confirmînd prin rezultatele obţinute la concursurile şcolare, raionale,
naţionale şi internaţionale că informatica în ţara noastră este un domeniu de perspectivă, care ne
permite să ne realizăm aspiraţiile şi visurile noastre. Sînt sigur, că problemele propuse de profesori,
rezolvările originale elaborate de elevi, schimbul de idei şi de opinii au extins şi au consolidat
spaţiul informaţional şcolar, au contribuit la perfecţionarea metodelor de predare-învăţare a
informaticii.
Dorim tuturor participanţilor la Olimpiadă noi succese şi sperăm şi în continuare la o
conlucrare eficientă în domeniul edificării societăţii informaţionale.
Anatol Gremalschi,
doctor habilitat,
profesor universitar
2
Instrucţiune
I. Fişiere şi directoare
I.1. După terminarea lucrului, participantul va transmite oficialului numai fişierele sursă ale
programelor, câte unul pentru fiecare problemă.
I.2. Fişierele sursă vor avea acelaşi nume ca şi fişierele de intrare/ieşire. De exemplu în cazul
fişierelor de intrare/ieşire numite SORTARE.IN şi SORTARE.OUT, fişierul sursă se va numi
SORTARE.PAS, SORTARE.C, sau SORTARE.CPP.
I.3. Fişierele sursă se vor afla într-un director cu numele format din primele 7 litere ale
numelui de familie, plus prima literă a numelui. De exemplu pentru elevul Tudor Mărgineanu,
directorul se va numi MARGINET. Pentru elevul Tudor Roşu, directorul se va numi ROSUT.
Denumirile de directoare sînt indicate în fişa de înregistrare.
II.3. Programul nu va scrie nimic pe ecran, nu va citi nimic de la tastatura, nu va lucra cu alte
fişiere, cu excepţia cazurilor în care se indică altfel în enunţul problemei.
III.2. Programul Pascal va utiliza numai unit-ul system care este încărcat de compilator automat.
Includerea altor unit-uri prin utilizarea clausei uses este interzisă, cu excepţia cazurilor special
prevăzute în enunţul problemei.
III.3. Soluţiile competitorilor nu vor include nici un fel de directive de compilare care alterează
opţiunile prestabilite de compilare.
III.4. Competitorilor le vor fi puse la dispoziţie fişierele C_PAS.BAT, C_CPP.BAT care vor
compila soluţiile lor cu exact aceleaşi opţiuni ca şi în cadrul evaluării. Mediile integrate vor fi
configurate pentru a reflecta cât mai precis opţiunile de compilare din cadrul evaluării.
IV.2. În cazul încălcării cerinţelor de mai sus, participantul poate fi sancţionat cu anularea
rezultatului pentru problema în cauză, pentru ziua respectivă, sau cu înlăturarea de la competiţie.
3
Descrierea problemelor − Ziua 1
Clasele 7 - 9
ECHIPE.PAS
Echipe 100 ECHIPE.C ECHIPE.IN ECHIPE.OUT
ECHIPE.CPP
PUNCTE.PAS
Puncte 100 PUNCTE.C PUNCTE.IN PUNCTE.OUT
PUNCTE.CPP
Total 300 - - -
4
Calcule
CALCULE.IN CALCULE.OUT
23+01-333+000 -309
Restricţii. Un număr poate conţine cel mult cinci cifre. O expresie poate include cel mult 250
de caractere. Timpul de execuţie nu va depăşi o secundă. Fişierul sursă va avea denumirea
CALCULE.PAS, CALCULE.C sau CALCULE.CPP.
5
Rezolvare
Valoarea expresiei poate fi calculată printr-o singură parcurgere a şirului de caractere, citit din
fişierul de intrare. În procesul parcurgerii, unităţile gramaticale <Număr> din componenţa şirului
de caractere, sînt transformate în numere întregi. Pentru a evita erorile de depăşre, se va folosi tipul
de date longint.
Program Calcule;
{ Clasele 7-9 }
var S : string;
n : longint;
Intrare, Iesire : text;
procedure Citire;
begin
assign(Intrare, 'CALCULE.IN');
reset (Intrare);
readln(Intrare, S);
close(Intrare);
end; { Calcule }
procedure Scrie;
begin
assign(Iesire, 'CALCULE.OUT');
rewrite(Iesire);
writeln(Iesire, n);
close(Iesire);
end; { Scrie }
procedure Evaluare;
var Numar : string;
i : integer;
Semn : char;
begin
Numar:='';
i:=1;
while (S[i] in ['0'..'9']) and (i<= length(S)) do
begin
Numar:=Numar+S[i];
i:=i+1;
end; { while }
n:=Valoare(Numar);
while i<=length(S) do
begin
Semn:=S[i];
i:=i+1;
Numar:='';
while (S[i] in ['0'..'9']) and (i<= length(S)) do
begin
Numar:=Numar+S[i];
i:=i+1;
end;
6
case Semn of
'+': n:=n+Valoare(Numar);
'-': n:=n-Valoare(Numar);
end; { case }
end; { while }
end; { Evaluare }
begin
Citire;
Evaluare;
Scrie;
end.
7
Echipe
O companie a hotărît să formeze o echipă care trebuie să incudă exact trei persoane. La
concursul de selecţie a echipei s-au înscris n participanţi. Selecţia echipei se efectuează în felul
următor:
1. Iniţial participanţii la concurs sînt împărţiţi în două subgrupe. În acest scop, participanţii sînt
numerotaţi prin 1, 2, ..., n. În una din subgrupe se includ participanţii cu numere pare, iar în
cealaltă − cei cu numere impare.
2. În continuare se analizează fiecare din subgrupele obţinute. Sînt posibile următoarele cazuri:
a) dacă subgrupa curentă conţine exact trei persoane, ele vor forma echipa respectivă;
b) dacă subgrupa curentă conţine una sau două persoane, ea este exclusă din studiu;
c) dacă subgrupa curentă conţine mai mult de trei persoane, ele din nou vor fi numerotate
prin 1, 2, 3 ş.a.m.d. şi împărţite în două subgrupe: cei cu numerele pare vor fi încluse
într-o subgrupă, iar cei cu numerele impare − în alta.
3. Procesul de divizare în subgrupe se termină atunci cînd a fost găsită o echipă sau cînd toate
subgrupele obţinute conţin mai puţin de trei persoane.
Elaboraţi un program care calculează numărul variantelor posibile de selecţie a echipei. Dacă
echipa respectivă nu poate fi selectată prin metoda descrisă mai sus, prin definiţie, numărul
variantelor posibile este egal cu zero.
Date de intrare. Fişierul text ECHIPE.IN conţine pe o singură linie numărul natural n.
Date de ieşire. Fişierul text ECHIPE.OUT va conţine pe o singură linie numărul variantelor
posibile de selecţie a echipei.
Exemplu.
ECHIPE.IN ECHIPE.OUT
11 3
8
Rezolvare
Prin V (n) vom nota funcţia ce reprezintă numărul variantelor posibile de selectare a echipei
dintr-o grupă formată din n participanţi. Prin calcule directe ne putem convinge că V (1) = 0 ,
V (2) = 0 şi V (3) = 1 . Evident, pentru orice n, n > 3 , sînt posibile următoarele cazuri:
1. Numărul n este par. În rezultatul divizării grupei curente de participanţi vom obţine două
subgrupe cu cîte (n div 2) participanţi. Evident, în acest caz:
9
Pentru a estima complexitatea temporală a programului Echipe, vom examina cel mai
nefavorabil caz, în care toate apelurile recursive ale funcţiei V(n) se realiza numai prin execuţia
instrucţiunii de atribuire V:=V(n div 2)+V((n div 2)+1).
Evident, în astfel de cazuri, numărul maximal de apeluri recursive Q ale funcţiei V(n)
satisface inegalitatea:
Q ≤ 1 + 21 + 2 2 + K + 2 k ,
10
Puncte
Se consideră punctele P1, P2, P3, ..., Pn pe un plan cartezian. Fiecare punct Pi, i = 1, 2 , ..., n ,
este definit prin coordonatele sale întregi (xi, yi).
Elaboraţi un program care construieşte un dreptunghi de arie minimă cu laturile paralele cu
axele de coordonate, ce conţine toate punctele P1, P2, P3, ..., Pn. Evident, orice punct de pe latura
dreptunghiului aparţine acestuia.
Date de intrare. Fişierul text PUNCTE.IN conţine pe prima linie numărul natural n. Fiecare
din următoarele n linii ale fişierului de intrare conţine cîte două numere întregi separate prin spaţiu.
Linia i + 1 a fişierului de intrare conţine coordonatele xi, yi ale punctului Pi.
Date de ieşire. Fişierul text PUNCTE.OUT va conţine două linii. Pe prima linie se scriu
coordonatele întregi (a, b) ale colţului stînga-jos, iar pe linia a doua − coordonatele întregi (c, d) ale
colţului dreapta-sus ale dreptunghiului construit. Numerele respective vor fi separate prin spaţiu.
Exemplu.
PUNCTE.IN PUNCTE.OUT
4 1 2
2 6 4 7
1 2
3 7
4 5
11
Rezolvare
Fig. 1
Program Puncte;
{ Clasele 7-9 }
const
nmax=1000; { numarul maximal de puncte }
ValoareaMaxima=1000;
ValoareaMinima=0;
var n : integer;
a, b, c, d : integer;
X, Y : array[1..nmax] of integer;
procedure Citire;
var Intrare : text;
i : integer;
begin
assign(Intrare, 'PUNCTE.IN');
reset(Intrare);
readln(Intrare, n);
for i:=1 to n do
readln(Intrare, X[i], Y[i]);
close(Intrare);
end; { Citire }
12
procedure Scrie;
var Iesire : text;
begin
assign(Iesire, 'PUNCTE.OUT');
rewrite(Iesire);
writeln(Iesire, a, ‘ ‘, b);
writeln(Iesire, c, ‘ ‘, d);
close(Iesire);
end; { Scrie }
procedure Dreptunghiuri;
var i : integer;
begin
{ calculam minimul din X[i] }
a:=ValoareaMaxima;
for i:=1 to n do
if X[i]<a then a:=X[i];
{ calculam minimumul din Y[i] }
b:=ValoareaMaxima;
for i:=1 to n do
if Y[i]<b then b:=Y[i];
{ calculam maximumul din X[i] }
c:=ValoareaMinima;
for i:=1 to n do
if X[i]>c then c:=X[i];
{ calculam maximumul din Y[i] }
d:=ValoareaMinima;
for i:=1 to n do
if Y[i]>d then d:=Y[i];
end; { Dreptunghiuri }
begin
Citire;
Dreptunghiuri;
Scrie;
end.
13
Descrierea problemelor − Ziua 1
Clasele 10 - 12
ECHIPE.PAS
Echipe 100 ECHIPE.C ECHIPE.IN ECHIPE.OUT
ECHIPE.CPP
PUNCTE.PAS
Puncte 100 PUNCTE.C PUNCTE.IN PUNCTE.OUT
PUNCTE.CPP
Total 300 - - -
14
Calcule
CALCULE.IN CALCULE.OUT
2.3+0.1-33.3+0.00 -3.0900000000E+01
Restricţii. Întregul fără semn poate conţine cel mult cinci cifre. O expresie poate include cel
mult 250 de caractere. Timpul de execuţie nu va depăşi o secundă. Fişierul sursă va avea denumirea
CALCULE.PAS, CALCULE.C sau CALCULE.CPP.
15
Rezolvare
Valoarea expresiei poate fi calculată printr-o singură parcurgere a şirului de caractere, citit din
fişierul de intrare. În procesul parcurgerii, unităţile gramaticale <Real fără semn> din componenţa
şirului de caractere, sînt transformate în numere reale.
Program Calcule;
{ Clasele 10-12 }
var S : string;
x : real;
Intrare, Iesire : text;
procedure Citire;
begin
assign(Intrare, 'CALCULE.IN');
reset (Intrare);
readln(Intrare, S);
close(Intrare);
end; { Calcule }
procedure Scrie;
begin
assign(Iesire, 'CALCULE.OUT');
rewrite(Iesire);
writeln(Iesire, x);
close(Iesire);
end; { Scrie }
procedure Evaluare;
var Numar : string;
i : integer;
Semn : char;
begin
Numar:='';
i:=1;
while (S[i] in ['0'..'9', '.']) and (i<= length(S)) do
begin
Numar:=Numar+S[i];
i:=i+1;
end; { while }
x:=Valoare(Numar);
while i<=length(S) do
begin
Semn:=S[i];
i:=i+1;
Numar:='';
while (S[i] in ['0'..'9', '.']) and (i<= length(S)) do
begin
Numar:=Numar+S[i];
i:=i+1;
end;
case Semn of
16
'+': x:=x+Valoare(Numar);
'-': x:=x-Valoare(Numar);
end; { case }
end; { while }
end; { Evaluare }
begin
Citire;
Evaluare;
Scrie;
end.
17
Echipe
O companie a hotărît să formeze o echipă care trebuie să incudă exact trei persoane. La
concursul de selecţie a echipei s-au înscris n participanţi. Selecţia echipei se efectuează în felul
următor:
4. Iniţial participanţii la concurs sînt împărţiţi în două subgrupe. În acest scop, participanţii sînt
numerotaţi prin 1, 2, ..., n. În una din subgrupe se includ participanţii cu numere pare, iar în
cealaltă − cei cu numere impare.
5. În continuare se analizează fiecare din subgrupele obţinute. Sînt posibile următoarele cazuri:
d) dacă subgrupa curentă conţine exact trei persoane, ele vor forma echipa respectivă;
e) dacă subgrupa curentă conţine una sau două persoane, ea este exclusă din studiu;
f) dacă subgrupa curentă conţine mai mult de trei persoane, ele din nou vor fi numerotate
prin 1, 2, 3 ş.a.m.d. şi împărţite în două subgrupe: cei cu numerele pare vor fi încluse
într-o subgrupă, iar cei cu numerele impare − în alta.
6. Procesul de divizare în subgrupe se termină atunci cînd a fost găsită o echipă sau cînd toate
subgrupele obţinute conţin mai puţin de trei persoane.
Elaboraţi un program care calculează numărul variantelor posibile de selecţie a echipei. Dacă
echipa respectivă nu poate fi selectată prin metoda descrisă mai sus, prin definiţie, numărul
variantelor posibile este egal cu zero.
Date de intrare. Fişierul text ECHIPE.IN conţine pe o singură linie numărul natural n.
Date de ieşire. Fişierul text ECHIPE.OUT va conţine pe o singură linie numărul variantelor
posibile de selecţie a echipei.
Exemplu.
ECHIPE.IN ECHIPE.OUT
11 3
18
Rezolvare
Prin V (n) vom nota funcţia ce reprezintă numărul variantelor posibile de selectare a echipei
dintr-o grupă formată din n participanţi. Prin calcule directe ne putem convinge că V (1) = 0 ,
V (2) = 0 şi V (3) = 1 . Evident, pentru orice n, n > 3 , sînt posibile următoarele cazuri:
1. Numărul n este par. În rezultatul divizării grupei curente de participanţi vom obţine două
subgrupe cu cîte (n div 2) participanţi. Evident, în acest caz:
procedure Citire;
begin
assign(Intrare,'ECHIPE.IN');
reset(Intrare);
readln(Intrare, n);
close(Intrare);
end; { Citire }
procedure Scrie;
begin
assign(Iesire, 'ECHIPE.OUT');
rewrite(Iesire);
writeln(Iesire, Variante);
close(Iesire);
end; { Scrie }
function V(n : longint) : longint;
begin
if n<3 then V:=0 else
if n=3 then V:=1 else
if odd(n) then V:=V(n div 2)+V((n div 2)+1)
else V:=2*V(n div 2)
end; { Var }
begin
Citire;
Variante:=V(n);
Scrie;
end.
19
Pentru a estima complexitatea temporală a programului Echipe, vom examina cel mai
nefavorabil caz, în care toate apelurile recursive ale funcţiei V(n) se realiza numai prin execuţia
instrucţiunii de atribuire V:=V(n div 2)+V((n div 2)+1).
Evident, în astfel de cazuri, numărul maximal de apeluri recursive Q ale funcţiei V(n)
satisface inegalitatea:
Q ≤ 1 + 21 + 2 2 + K + 2 k ,
20
Puncte
Se consideră punctele P1, P2, P3, ..., Pn pe un plan cartezian. Fiecare punct Pi, i = 1, 2 , ..., n ,
este definit prin coordonatele sale reale (xi, yi).
Elaboraţi un program care construieşte un dreptunghi de arie minimă cu laturile paralele cu
axele de coordonate, ce conţine toate punctele P1, P2, P3, ..., Pn. Evident, orice punct de pe latura
dreptunghiului aparţine acestuia.
Date de intrare. Fişierul text PUNCTE.IN conţine pe prima linie numărul natural n. Fiecare
din următoarele n linii ale fişierului de intrare conţine cîte două numere reale separate prin spaţiu.
Linia i + 1 a fişierului de intrare conţine coordonatele xi, yi ale punctului Pi.
Date de ieşire. Fişierul text PUNCTE.OUT va conţine două linii. Pe prima linie se scriu
coordonatele (a, b) ale colţului stînga-jos, iar pe linia a doua − coordonatele (c, d) ale colţului
dreapta-sus ale dreptunghiului construit. Numerele respective vor fi scrise fără specificatori de
format şi separate prin spaţiu.
Exemplu.
PUNCTE.IN PUNCTE.OUT
4 1.0000000000E+00 2.0000000000E+00
2.0 6.0 4.0000000000E+00 7.0000000000E+00
1.0 2.0
3.0 7.0
4.0 5.0
21
Rezolvare
Fig. 1
Program Puncte;
{ Clasele 10-12 }
const
nmax=10000; { numarul maximal de puncte }
ValoareaMaxima=70000;
ValoareaMinima=0;
type Tablou = array[1..nmax] of real;
var n : integer;
a, b, c, d : real;
X, Y :^Tablou;
procedure Citire;
var Intrare : text;
i : integer;
begin
assign(Intrare, 'PUNCTE.IN');
reset(Intrare);
22
readln(Intrare, n);
for i:=1 to n do
readln(Intrare, X^[i], Y^[i]);
close(Intrare);
end; { Citire }
procedure Scrie;
var Iesire : text;
begin
assign(Iesire, 'PUNCTE.OUT');
rewrite(Iesire);
writeln(Iesire, a,' ', b);
writeln(Iesire, c,' ', d);
close(Iesire);
end; { Scrie }
procedure Dreptunghiuri;
var i : integer;
begin
{ calculam minimul din X[i] }
a:=ValoareaMaxima;
for i:=1 to n do
if X^[i]<a then a:=X^[i];
{ calculam minimumul din Y[i] }
b:=ValoareaMaxima;
for i:=1 to n do
if Y^[i]<b then b:=Y^[i];
{ calculam maximumul din X[i] }
c:=ValoareaMinima;
for i:=1 to n do
if X^[i]>c then c:=X^[i];
{ calculam maximumul din Y[i] }
d:=ValoareaMinima;
for i:=1 to n do
if Y^[i]>d then d:=Y^[i];
end; { Dreptunghiuri }
begin
new(X); new(Y);
Citire;
Dreptunghiuri;
Scrie;
dispose(X); dispose(Y);
end.
23
Descrierea problemelor − Ziua 2
Clasele 7 - 9
MODEME.PAS
Modeme 100 MODEME.C MODEME.IN MODEME.OUT
MODEME.CPP
CANGUR.PAS
Cangurul 100 CANGUR.C CANGUR.IN CANGUR.OUT
CANGUR.CPP
Total 300 - - -
24
Păsări călătoare
O colonie de păsări călătoare este formată din n cuiburi, notate prin C1, C2, ..., Ci, ..., Cn.
Fiecare cuib Ci este definit prin coordonatele întregi carteziene xi, yi şi numărul de păsări mi ce
locuiesc în el.
Pentru a migra spre sud, într-o zi de toamnă, păsările se adună într-un singur stol,
coordonatele întregi carteziene ale căruia sînt notate prin xs, ys. Evident, pentru a ajunge din cuibul
Ci în punctul de formare a stolului, fiecare pasăre din acest cuib trebuie să parcurgă în zbor distanţa
dintre punctele (xi, yi) şi (xs, ys):
d i = ( xi − x s ) 2 + ( y i − y s ) 2 .
Pentru a economisi puterile înainte de o călătorie foarte lungă, instinctiv, păsările aleg
coordonatele xs, ys ale punctului de formare a stolului în aşa mod, încît suma S a distanţelor parcurse
în zbor de toate păsările coloniei să fie minimă.
Elaboraţi un program care calculează suma minimă S.
Date de intrare. Fişierul text PASARI.IN conţine pe prima linie numărul natural n. Fiecare
din următoarele n linii ale fişierului de intrare conţine cîte trei numere întregi separate prin spaţiu.
Linia i + 1 a fişierului de intrare conţine numerele xi, yi, mi.
Date de ieşire. Fişierul text PASARI.OUT va conţine pe o singură linie numărul real S scris
fără specificatori de format.
Exemplu.
PASARI.IN PASARI.OUT
4 1.1313708499E+01
1 1 1
5 1 1
5 5 1
1 5 1
25
Rezolvare
Conform restricţiilor problemei, toate punctele ce simbolizează cuiburile C1, C2, ..., Ci, ..., Cn
aparţin unui pătrat cu laturile paralele axelor de coordonate şi vîrfurile (1, 1), (10, 1), (10, 10), (1,
10). Evident, punctul de formare a stolului (xs, ys) aparţine, de asemenea, aceluiaşi pătrat (Fig. 1).
y
10 Cuib
* Stol
...
*
...
2
1
0 1 2 ... ... 10 x
Fig. 1
Pentru a determina suma minimă S vom folosi metoda trierii, calculînd consecutiv pentru
fiecare punct cu coordonatele întregi (xs, ys), 1 ≤ x s ≤ 10 şi 1 ≤ y s ≤ 10 , suma distanţelor
n
S = ∑ mi d i ,
i =1
Program Pasari;
{ Clasele 7-9 }
const Dimensiuni=100;
nmax=50;
var n : integer;
X, Y, M : array[1..nmax] of integer;
S : real;
Intrare, Iesire : text;
procedure Citire;
{ Citirea datelor din fisierul de intrare }
var i : integer;
begin
assign(Intrare, 'PASARI.IN');
reset(Intrare);
readln(Intrare, n);
for i:=1 to n do readln(Intrare, X[i], Y[i], M[i]);
close(Intrare);
end; { Citire }
26
procedure Scrie;
{ Scrierea datelor in fisierul de iesire }
begin
assign(Iesire, 'PASARI.OUT');
rewrite(Iesire);
writeln(Iesire, S);
close(Iesire);
end; { Scrie }
procedure SumaMinima;
var xs, ys : integer;
r : real;
begin
S:=1.7E38; { valoarea maximal admisibila a sumei distantelor }
for xs:=1 to Dimensiuni do
for ys:=1 to Dimensiuni do
begin
r:=Suma(xs, ys);
if r<S then S:=r;
end;
end; { SumaMinima }
begin
Citire;
SumaMinima;
Scrie;
end.
27
Modeme
Restricţii. 2 ≤ n ≤ 500 000 . Timpul de execuţie nu va depăşi o secundă. Fişierul sursă va avea
denumirea MODEME.PAS, MODEME.C sau MODEME.CPP.
28
Rezolvare
Mai întîi se găseşte un număr prim p mai mare decît numărul n. Evident, perechile { p − n, n}
şi { p − n + 1, n − 1} sînt perechi care au suma respectivă egală cu p şi satisfac condiţiilor problemei.
De asemenea, toate numerele de la p − n pînă la n tot vor forma astfel de perechi.
În continuare, soluţionam aceiaşi problemă pentru o nouă valoare a lui n, şi anume, pentru
p − n − 1 . Acest proces se va repeta atît timp, cît valoarea curentă a lui n este mai mare ca unu. În
rezultat obţinem mulţimea care este formată din toate perechile posibile ce corespund condiţiilor din
enunţul problemei.
Program Modeme;
var n : longint;
Intrare, Iesire : text;
procedure Citire;
begin
assign(Intrare, 'MODEME.IN');
reset(Intrare);
readln(Intrare, n);
close(Intrare);
end; { Citire }
Procedure Perechi;
var i, j, p : longint;
begin
while n>1 do
begin
p:=n+1;
while not NumarPrim(p) do p:=p+1;
i:=p-n;
j:=n;
n:=i-1;
while i<j do
begin
writeln(Iesire,i,' ',j);
i:=i+1;
j:=j-1
end;
end;
end; { Perechi }
begin
Citire;
assign(Iesire,'MODEME.OUT');
rewrite(Iesire);
Perechi;
close(Iesire);
end.
29
Menţionăm, că funcţia NumarPrim realizează metoda “forţei brute”, calculînd prin triere
numărul de divizori ai argumentului k. Prin măsurări directe ne putem convinge că pentru
n = 500 000 timpul de execuţie poate depăşi valoarea de o secundă. Pentru a evita astfel de situaţii,
vom utiliza un algoritm mai “inteligent”, şi anume:
30
Cangurul
n ... 4 3 2 1
- cangurul - izvorul
Fig. 1
Întrucît cangurul nu întotdeauna ţine minte unde se află izvorul, înaintea fiecărui salt el decide
în care direcţie va face saltul respectiv: înainte sau înapoi. Este cunoscut faptul că după k salturi
cangurul a ajuns la izvor.
Elaboraţi un program, care calculează numărul de variante posibile de deplasare a cangurului
din poziţia iniţială la izvor.
Date de intrare.
Fişierul text CANGUR.IN conţine pe o singură linie numerele naturale n şi k, separate prin
spaţiu.
Date de ieşire.
Fişierul text CANGUR.OUT va conţine pe o singură linie un număr întreg − numărul de variante
posibile de deplasare a cangurului din poziţia iniţială la izvor.
Exemplu.
CANGUR.IN CANGUR.OUT
2 4 2
31
Rezolvare
A[i]
A[i+1] A[i-1]
t
i-1 i i+1
B[i]
t+1
i-1 i i+1
- cangurul - izvorul
Fig. 2
Într-un mod similar, introducem în studiu tabloul B. Elementul B[i] al acestui tablou
reprezintă numărul de variante posibile de deplasare a cangurului din poziţia aflată la distanţa i la
izvor prin t+1 salturi (Fig. 2). Cunoscînd elementele tabloului A, putem calcula elementele
tabloului B:
B[i]:=A[i-1]+A[i+1], i=1, 2, 3, ..., n+k.
Pentru a evita verificările în cazurile în care i=1 sau i=n+k, prin definiţie stabilim A[0]=0,
A[n+k+1]=0 şi B[0]=0, B[n+k+1]=0.
Evident, pentru a afla numărul de variante posibile de deplasare a cangurului la izvor prin t+2
salturi, stabilim A:=B şi calculăm din nou elementele tabloului B. Prin urmare, calculînd elementele
tablourilor A şi B pentru t=2, 3, ..., k, vom afla şi valoarea A[n], care reprezintă soluţia problemei.
Program Cangurul;
const nmax=37;
kmax=37;
var
n, k : integer;
A, B : array [0..nmax+kmax+1] of longint;
Intrare, Iesire : text;
procedure Citire;
begin
assign(Intrare, 'CANGUR.IN');
reset(Intrare);
readln(Intrare, n, k);
close(Intrare);
end; { Citire }
32
procedure Scrie;
begin
assign(Iesire, 'CANGUR.OUT');
rewrite(Iesire);
writeln(Iesire, A[n]);
close(Iesire);
end; { Citire }
procedure Salturi;
var i, t : integer;
begin
{ zerografiem tablourile A si B }
for i:=0 to n+k+1 do
A[i]:=0;
B:=A;
{ calcule recurente }
A[1]:=1;
for t:=2 to k do
begin
for i:=1 to n+k do
B[i]:=A[i-1]+A[i+1];
A:=B;
end; { for }
end; { Salturi }
begin
Citire;
Salturi;
Scrie;
end.
33
Descrierea problemelor − Ziua 2
Clasele 10 - 12
GENE.PAS
Gene 100 GENE.C GENE.IN GENE.OUT
GENE.CPP
CANGUR.PAS
Cangurul 100 CANGUR.C CANGUR.IN CANGUR.OUT
CANGUR.CPP
Total 300 - - -
34
Biliard
Y
- bila
- tacul
- gaura cu pungă
- traiectoria bilei
X
0 a
Fig. 1
Pe masa de joc se află o bilă punctiformă cu coordonatele (xb, yb). În colţul stînga-jos masa de
biliard are o gaură punctiformă, prin care, în cazul unei lovituri reuşite cu tacul, bila cade într-o
pungă. Se consideră că forţa de frecare este egală cu zero, iar loviturile bilei de marginile mesei se
produc conform legii “unghiul de cădere este egal cu unghiul de reflexie”. În Biliardul Matematic
se consideră reuşită doar acea lovitură cu tacul, după care, înainte de a cădea în pungă, bila se
loveşte de marginile mesei exact de k ori, k ≥ 1 . Conform regulilor jocului, colţurile mesei nu
aparţin nici la una din margini. Lovitura cu tacul se defineşte prin coordonatele (xi, yi) ale punctului
de pe marginea mesei, în care bila va lovi pentru prima oară.
Elaboraţi un program, care, cunoscînd coordonatele (xb, yb) şi numărul de lovituri k ale bilei
de marginile mesei, calculează toate variantele posibile de lovituri reuşite cu tacul.
Date de intrare. Fişierul text BILIARD.IN conţine pe prima linie numerele reale a, b,
separate prin spaţiu. Linia a doua a fişierului de intrare conţine numerele reale xb, yb, separate prin
spaţiu. Linia a treia a fişierului de intrare conţine numărul întreg k.
Date de ieşire. Fişierul text BILIARD.OUT va conţine pe prima linie un număr întreg n −
numărul variantele posibile de lovituri reuşite cu tacul. Fiecare din următoarele n linii ale fişierului
de ieşire va conţine cîte două numere reale scrise fără specificatori de format şi separate prin spaţiu.
Linia i + 1 a fişierului de ieşire va conţine coordonatele xi, yi ce definesc o lovitură reuşită cu tacul.
Exemplu.
BILIARD.IN BILIARD.OUT
3.0 2.0 3
1.5 1.5 0.0000000000E+00 1.2000000000E+00
2 1.0909090909E+00 0.0000000000E+00
2.4000000000E+00 2.0000000000E+00
35
Rezolvare
Pentru a studia mişcarea bilei, introducem în studiu mese virtuale de joc. Prima masă virtuală
se obţine prin reflexia în “oglindă” a mesei de biliard, oglinda respectivă fiind formată de marginea
de care bila s-a lovit pentru prima oară (Fig. 2).
Fig. 2
A doua masă virtuală se obţine prin reflexia în “oglindă” a primei mese virtuale, “oglinda”
respectivă fiind formată de marginea virtuală de care bila s-a lovit a doua oară. Evident, într-un mod
similar, pot fi obţinute toate cele k mese virtuale de joc.
Întrucît loviturile bilei de marginile mesei se produc conform legii “unghiul de cădere este
egal cu unghiul de reflexie”, traectoria bilei pe mesele virtuale reprezintă o linie dreaptă, care
reuneşte punctul cu coordonatele (xb, yb) cu imaginea în “oglindă” a găurii punctiforme.
Prin urmare, pentru a determina toate variantele posibile de lovituri reuşite cu tacul, divizăm
planul cartezian în dreptunghiuri de dimensiunile a × b şi plasăm în punctele cu coordonatele
( 2ia , 2 jb ) , − k ≤ i, j ≤ k , imagini ale găurii punctiforme. În continuare, reunim prin linii drepte
punctul (xb, yb) cu fiecare din punctele ( 2ia , 2 jb ) şi selectăm segmentele care intersectează exact k
laturi ale dreptunghiurilor respective. Evident, nu vor fi examinate dreptele care trec prin vîrfuri de
dreptunghiuri.
Program Biliard;
const kmax=25;
var a, b, { dimensiunile mesei }
x, y : real; { coordonatele bilei }
k, { numarul cerut de lovituri de marginile mesei }
m : integer; { numarul de lovituri reusite cu tacul }
L : array [1..kmax, 1..2] of real; { loviturile reusite cu tacul}
Intrare, Iesire : text;
procedure Citeste;
begin
assign(Intrare,'BILIARD.IN');
reset(Intrare);
readln(Intrare, a, b);
readln(Intrare, x, y);
readln(Intrare, k);
close(Intrare);
36
end; {Citeste }
procedure Scrie;
var i : integer;
begin
assign(Iesire,'BILIARD.OUT');
rewrite(Iesire);
writeln(Iesire, m);
for i:=1 to m do
writeln(Iesire, L[i, 1],' ',L[i, 2]);
close(Iesire);
end; { Scrie }
procedure LovituriReusite;
var i, j : integer;
c, d : real; { coordonatele gaurilor virtuale }
dir : integer;
t, u : real;
trece : boolean;
begin
m:=0; { numarul curent de lovituri reusite cu tacul }
for i:=-k to k do
for j:=-k to k do
begin
c:=2*i*a; d:=2*j*b;
if trunc(abs(c-x)/a)+trunc(abs(d-y)/b)=k then
begin
{ verifica sa nu treaca prin nici un alt virf }
if c>x then dir:=-1 else dir:=1;
t:=c+dir*a;
trece:=false;
while dir*t<dir*x do
begin
u:=y+(d-y)*(t-x)/(c-x);
u:=u/b;
if (abs(u-round(u))<0.00001) then trece:=true;
t:=t+dir*a;
end; { while }
if not(trece) then
begin
m:=m+1;
if (((b-y)*(c-x)-(a-x)*(d-y))*((b-y)*(0-x)-(a-x)*(b-y))>=0) and
(((b-y)*(c-x)-(0-x)*(d-y))*((b-y)*(a-x)-(0-x)*(b-y))>0) then
begin L[m, 1]:=x+(c-x)*(b-y)/(d-y); L[m, 2]:=b; end;
if (((0-y)*(c-x)-(a-x)*(d-y))*((0-y)*(a-x)-(a-x)*(b-y))>=0) and
(((b-y)*(c-x)-(a-x)*(d-y))*((b-y)*(a-x)-(a-x)*(0-y))>0) then
begin L[m, 1]:=a; L[m, 2]:=y+(d-y)*(a-x)/(c-x); end;
if (((0-y)*(c-x)-(0-x)*(d-y))*((0-y)*(a-x)-(0-x)*(0-y))>=0) and
(((0-y)*(c-x)-(a-x)*(d-y))*((0-y)*(0-x)-(a-x)*(0-y))>0) then
begin L[m, 1]:=x+(c-x)*(0-y)/(d-y); L[m, 2]:=0; end;
if (((0-y)*(c-x)-(0-x)*(d-y))*((0-y)*(0-x)-(0-x)*(b-y))>=0) and
(((b-y)*(c-x)-(0-x)*(d-y))*((b-y)*(0-x)-(0-x)*(0-y))>0) then
begin L[m, 1]:=0; L[m, 2]:=y+(d-y)*(0-x)/(c-x); end;
37
begin
Citeste;
LovituriReusite;
Scrie;
end.
Numărul de execuţii ale instrucţiunii compuse begin ... end din componenţa ciclurilor
imbricate for ale procedurii LovituriReusite este 4k 2 . Evident, pentru k ≤ 20 , timpul cerut de
această procedură va fi cu mult mai mic decît o secundă.
38
Gene
Se consideră o mulţime de gene G = {g1, g2, ..., gi, ..., gn}. În scopuri didactice, fiecare genă gi
din mulţimea G este reprezentată printr-un şir de caractere, format din literele mici ale alfabetului
latin. Toate şirurile de caractere din mulţimea G au aceiaşi lungime.
Prin definiţie, gena gk este rezultatul încrucişării genelor gi, gj (acest fapt se notează prin
gi • gj → gk), dacă şirul de caractere gk poate fi obţinut din şirurile de caractere gi, gj prin înlocuirea
unui prefix din şirul gi (gj) cu un prefix de aceeaşi lungime din şirul gj (gi).
De exemplu, abcde • xyztv → xycde, întrucît şirul xycde poate fi obţinut din şirul
abcde prin înlocuirea prefixului ab cu prefixul xy din şirul xyztv.
Într-un mod similar, abcde • xyztv → abztv, întrucît şirul abztv poate fi obţinut din
şirul xyztv prin înlocuirea prefixului xy cu prefixul ab din şirul abcde.
Numim protopereche a mulţimii G orice pereche de gene {gi, gj}, gi ∈ G, gj ∈ G, pentru care
mulţimea G poate fi ordonată în forma:
(gi, gj, gm, ..., gk, ..., gq)
în aşa mod, încît fiecare genă gm, ..., gk, ..., gq este rezultatul încrucişării a două din genele
anterioare.
De exemplu, genele abcabc şi xymnpr sînt o protopereche a mulţimii:
G = {abcnpr, abmabc, xymnpr, aymabc, aycabc, xymabc, abcabc},
întrucît mulţimea în studiu poate fi ordonată în forma:
(abcabc, xymnpr, xymabc, abcnpr, abmabc, aymabc, aycabc),
unde
abcabc • xymnpr → xymabc;
abcabc • xymnpr → abcnpr;
xymabc • abcabc → abmabc;
xymabc • abcabc → aymabc;
aymabc • abcabc → aycabc.
Elaboraţi un program care calculează toate protoperechile posibile ale mulţimii G.
Date de intrare. Fişierul text GENE.IN conţine pe prima linie numărul natural n. Fiecare din
următoarele n linii ale fişierului de intrare conţine cîte un şir de caractere. Linia i + 1 a fişierului de
intrare conţine şirul de caractere ce reprezintă gena gi.
Date de ieşire. Fişierul text GENE.OUT va conţine protoperechile găsite, scrise în ordine
arbitrară. Fiecare protopereche {gi, gj} va ocupa două linii vecine: una din ele va conţine şirul de
caractere ce reprezintă gena gi, iar cealaltă − şirul de caractere ce reprezintă gena gj.
Exemplu.
GENE.IN GENE.OUT
7 abcabc
abcnpr xymnpr
abmabc abcnpr
xymnpr xymabc
aymabc
aycabc
xymabc
abcabc
39
Restricţii. 2 ≤ n ≤ 100 . Mulţimea G conţine cel puţin o protopereche. Lungimea oricărui şir
de caractere din fişierul de intrare este mai mică sau egală cu 100. Timpul de execuţie nu va depăşi
2 secunde. Fişierul sursă va avea denumirea GENE.PAS, GENE.C sau GENE.CPP.
Rezolvare
Demonstraţie. Fie
p = max(MaxPre f ( x, z ), MaxPref ( y, z )) ; t = max(MaxSuf ( x, z ), MaxSuf ( y, z )) .
Evident, p = MaxPref( x, z ) sau p = MaxPref( y, z ) . Fără a restrînge generalitea, putem
presupune că p = MaxPref ( x, z ) .
Într-un mod similar, t = MaxSuf ( x, z) sau t = MaxSuf ( y, z ) . Dacă t = MaxSuf ( x, z ) , atunci
concludem că x = z , fiindcă genele respective au un prefix şi sufix comun de lungime mai mare sau
egală cu length ( z ) . Dacă t = MaxSuf( y, z ) , atunci, luînd prefixul de lungime p din x şi sufixul de
lungime length ( z ) - p din y, prin încrucişare, obţinem gena z.
Reciproc, daca x şi y pot produce prin încrucişare gena z, atunci există un p, un prefix din x de
lungime p şi un sufix din y de lungime length ( z ) - p , care formează gena z (sau un prefix de lugime
p din y şi un sufix din x de lungime length ( z ) - p , dar acest caz se examinează în acelaşi mod).
Deci, MaxPref ( x, z ) ≥ p şi MaxSuf ( y,z ) ≥ length ( z ) - p , de unde rezultă inegalitatea din
enunţul afirmaţiei.
Fie S o mulţime nevidă de gene. Introducem în studiu funcţiile MaxPrefGen ( S, z ) şi
MaxSufGen( S, z ) , valorile cărora reprezintă lugimile celui mai lung prefix comun, respective sufix
comun, al genei z şi al unei gene din mulţimea S:
MaxPrefGen( S , z ) = max(MaxPref(xi , z )) ; MaxSufGen(S , z ) = max(MaxSuf(xi , z )) .
xi ∈S xi ∈S
Afirmaţia 2. Gena z este rezultatul încrucişării a două gene din muţimea S dacă şi numai dacă
MaxPrefGen ( S , z ) + MaxSufGen( S , z ) ≥ length( z ) .
40
MaxPref ( xi , z ) + MaxPref( x j , z ) ≥ z
sau
max(MaxPref(xi , z)) + max(MaxSuf(xi , z)) ≥ length(z ) ,
xi ∈S xi ∈S
41
genelor iniţiale. Astfel, în loc să testăm n2 perechi, vom testăm cel mult doar n perechi. Evident, în
acest caz complexitatea algoritmului va fi O(n 3 + n 2 length( x )) .
Program Gene;
{ Clasele 10-12 }
type Gena = string[100];
var S : array [1..100] of Gena;
n, i, j : integer;
x, y, s1, s2 : Gena;
MaxPrefVal, MaxSufVal : array[1..100, 1..100] of byte;
MaxPrefGen, MaxSufGen : array[1..101] of byte;
B : array[1..101] of boolean;
Intrare, Iesire : Text;
procedure Citire;
var i : integer;
begin
assign(Intrare, 'GENE.IN');
reset(Intrare);
readln(Intrare, n);
for i:=1 to n do
readln(Intrare, S[i]);
close(Intrare);
end; { Citire }
42
begin
s1:=s[1]; s2:=s1;
for i:=2 to n do
for j:=1 to length(s[1]) do
begin
if (s[i][j]<>s1[j])and(s[i][j]<>s2[j])
then { am gasit o litera noua pe pozitia j }
if s2[j]=s1[j] then s2[j]:=s[i][j] else
begin
{ am gasit deja o a treia litera pe pozitia j}
Verifica:=false;
exit;
end; { else }
end; { do }
Verifica:=true;
end; { Verifica }
procedure Precalculare;
{ Calculeaza functiile MaxPref }
var i, j : integer;
begin
for i:=1 to n-1 do
for j:=i+1 to n do
begin
MaxPrefVal[i,j]:=MaxPref(s[i],s[j]);
MaxPrefVal[j,i]:=MaxPrefVal[i,j];
MaxSufVal[i,j]:=MaxSuf(s[i],s[j]);
MaxSufVal[j,i]:=MaxSufVal[i,j];
end;
end; { Precalculare }
43
t:=1;
while (t<=n) and ((B[t])
or (MaxPrefGen[t] + MaxSufGen[t]<length(s[1]))) do inc(t);
if (t>n) then break; { nu am gasit }
{ daca am gasit include pe S[t] }
B[t]:=true;
{ Recalculeaza vectorii MaxPrefGen si MaxSufGen }
for q:=1 to n do
begin
if B[q] then continue;
MaxPrefGen[q]:=max(MaxPrefGen[q], MaxPrefVal[t,q]);
MaxSufGen[q]:=max(MaxSufGen[q], MaxSufVal[t,q]);
end;
end;
{ verifica daca am atins toate cuvintele }
for k:=1 to n do
if not(B[k]) then
begin
Protopereche:=false;
exit;
end;
Protopereche:=true;
end; { Protopereche }
begin
{ programul principal }
Citire;
if not(Verifica) then halt(0);
Precalculare;
assign(Iesire,'GENE.OUT');
rewrite(Iesire);
44
for i:=1 to n-1 do
begin
x:= s[i];
y:=conjugat(x);
j:=Cauta(y);
if j<i then continue;
if (Protopereche(i, j)) then
begin
writeln(Iesire, x);
writeln(Iesire, y);
end;
end;
close(Iesire);
end.
45
Cangurul
n ... 4 3 2 1
- cangurul - izvorul
Fig. 1
Întrucît cangurul nu întotdeauna ţine minte unde se află izvorul, înaintea fiecărui salt el decide
în care direcţie va face saltul respectiv: înainte sau înapoi. Este cunoscut faptul că după k salturi
cangurul a ajuns la izvor.
Elaboraţi un program, care calculează numărul de variante posibile de deplasare a cangurului
din poziţia iniţială la izvor.
Date de intrare.
Fişierul text CANGUR.IN conţine pe o singură linie numerele naturale n şi k, separate prin
spaţiu.
Date de ieşire.
Fişierul text CANGUR.OUT va conţine pe o singură linie un număr întreg − numărul de variante
posibile de deplasare a cangurului din poziţia iniţială la izvor.
Exemplu.
CANGUR.IN CANGUR.OUT
2 4 2
46
Rezolvare
A[i]
A[i+1] A[i-1]
t
i-1 i i+1
B[i]
t+1
i-1 i i+1
- cangurul - izvorul
Fig. 2
Într-un mod similar, introducem în studiu tabloul B. Elementul B[i] al acestui tablou
reprezintă numărul de variante posibile de deplasare a cangurului din poziţia aflată la distanţa i la
izvor prin t+1 salturi (Fig. 2). Cunoscînd elementele tabloului A, putem calcula elementele
tabloului B:
B[i]:=A[i-1]+A[i+1], i=1, 2, 3, ..., n+k.
Pentru a evita verificările în cazurile în care i=1 sau i=n+k, prin definiţie stabilim A[0]=0,
A[n+k+1]=0 şi B[0]=0, B[n+k+1]=0.
Evident, pentru a afla numărul de variante posibile de deplasare a cangurului la izvor prin t+2
salturi, stabilim A:=B şi calculăm din nou elementele tabloului B. Prin urmare, calculînd elementele
tablourilor A şi B pentru t=2, 3, ..., k, vom afla şi valoarea A[n], care reprezintă soluţia problemei.
Program Cangurul;
const nmax=37;
kmax=37;
var
n, k : integer;
A, B : array [0..nmax+kmax+1] of longint;
Intrare, Iesire : text;
procedure Citire;
begin
assign(Intrare, 'CANGUR.IN');
reset(Intrare);
readln(Intrare, n, k);
close(Intrare);
end; { Citire }
47
procedure Scrie;
begin
assign(Iesire, 'CANGUR.OUT');
rewrite(Iesire);
writeln(Iesire, A[n]);
close(Iesire);
end; { Citire }
procedure Salturi;
var i, t : integer;
begin
{ zerografiem tablourile A si B }
for i:=0 to n+k+1 do
A[i]:=0;
B:=A;
{ calcule recurente }
A[1]:=1;
for t:=2 to k do
begin
for i:=1 to n+k do
B[i]:=A[i-1]+A[i+1];
A:=B;
end; { for }
end; { Salturi }
begin
Citire;
Salturi;
Scrie;
end.
48