Olimpiada Republicană La Informatică Ediţia 2010 Moldova
February 10, 2023 | Author: Anonymous | Category: N/A
Short Description
Download Olimpiada Republicană La Informatică Ediţia 2010 Moldova...
Description
Ministerul Educaţiei al Republicii Moldova
Olimpiada Republicană la Informatică Ediţia 2010
Chişinău 2010
Olimpiada Republicană la Informatică. Ediţia 2010. 2010.
Lucrarea conţine problemele propuse la Olimpiada Republicană la Informatică a elevilor, ediţia 20 10. Pentru fiecare problemă sunt descrise algoritmul şi soluţia respectivă în limbajul de programare PASCAL. Materialele Olimpiadei pot fi de un real folos la studierea Informaticii atât el evilor, cât şi profesorilor de Informatică.
La elaborarea ediţiei au contribuit : Anatol Gremalschi, Ion Bolun, Iurie Mocanu, Viorel Afteni Dumitru Codreanu, Dumitru Ciubatîi, Bogdănaş Denis Constantin Dolghieru,
doctor habilitat, profesor universitar, UTM doctor habilitat, profesor universitar, ASEM Ministerul Educaţiei Ministerul Educaţiei Universitatea Bucureşti Universitatea Bucureşti Universitatea Tehnică, Iaşi Universitatea Politehnică, Bucureşti
2
Cuprins
CLASELE 7
9.......... ........... ........... ........... ........... ........... ........... ........... ........... ........... ....... 4
EXPRESII .................... ........................................... ............................................. ............................................ ............................................ ............................................. ....................... 5 IEPURAŞII ...................... ............................................. ............................................. ............................................ ............................................. .......................................... ................... 7 SEGMENTE .................... ........................................... ............................................. ............................................ ............................................. ........................................ ................. 11 PĂTRATE .................... ........................................... ............................................. ............................................ ............................................ ........................................... ..................... 15 NUMERE .................... ........................................... ............................................. ............................................ ............................................ ........................................... ..................... 17 LUPTA MARITIMĂ ...................... ............................................. .............................................. ............................................. ............................................ ............................ ...... 19 CLASELE 10
12 .......... ........... ........... ........... ........... .......... ........... ........... ........... ........... .. 22
VÂSLAŞII .................... ........................................... ............................................. ............................................ ............................................ ........................................... ..................... 23 IEPURAŞII ...................... ............................................. ............................................. ............................................ ............................................. ........................................ ................. 26 SEGMENTE .................... ........................................... ............................................. ............................................ ............................................. ........................................ ................. 30 ........................................... ............................................. ............................................ ............................................ ........................................... ..................... 35 ŞARPELE ....................
DESCOMPUNERI ..................... ............................................ ............................................. ............................................ ............................................ ................................ .......... 39 RADIOUL .................... ........................................... ............................................. ............................................ ............................................ ........................................... ..................... 42
3
Clasele 7 Denumirea problemei
Numărul de puncte alocat problemei
9
Denumirea fişierului sursă
Denumirea fişierului de intrare
Denumirea fişierului de ieşire
100
EXPRES.PAS EXPRES.C EXPRES.CPP
EXPRES.IN
EXPRES.OUT
Iepuraşii
100
IEPURE.PAS IEPURE.C IEPURE.CPP
IEPURE.IN
IEPURE.OUT
Segmente
100
SEGMENT.PAS SEGMENT.C SEGMENT.CPP
SEGMENT.IN
SEGMENT.OUT
100
PATRAT.PAS PATRAT.C PATRAT.CPP
PATRAT.IN
PATRAT.OUT
100
NUMERE.PAS NUMERE.C NUMERE.CPP
NUMERE.IN
NUMERE.OUT
LUPTA.IN
LUPTA.OUT
Expresii
Pătrate
Numere
LUPTA.PAS
Lupta maritimă
100
Total
600
LUPTA.C LUPTA.CPP
-
4
-
-
Expresii
Se consideră expresiile booleene de de forma: A B C D = Q
unde A, B, C, D şi Q sunt numere întregi fără semn, iar s1, s 2 şi s3 operatorii aritmetici +, - . Exemple: 17 57 ; 12 6 34 8 12 45 92 926 6 63 ; 21 2 4 6 21.
Elaboraţi un program care, cunoscând valorile numerelor A, B, C, D şi Q, selectează, dacă-i posibil, operatorii s1, s2 şi s3 în aşa mod, încât expresia booleană Sarcină.
A B C D = Q
să ia valoarea de adevăr true. Date de intrare. Fişierul text EXPRES.IN conţine A, B, C, D şi Q, separate prin spaţiu.
pe o singură linie numerele întregi
Date de ieşire. Fişierul text EXPRES.OUT va conţine pe o singură linie un
şir format format din operatorii s1, s2 şi s3. Dacă problema admite mai multe soluţii, în fişierul de ieşire se va scrie doar una, oricare din ele. Dacă problema nu are soluţie, în fişierul de ieşire se va scrie şirul de caractere ???. Exemplu1 1. EXPRES.IN 2 6 34 17 47
EXPRES.OUT -++
Exemplu1 2. EXPRES.IN
EXPRES.OUT
8 12 45 9 26
???
Timpul de execuţie nu va depăşi 0,1 secunde. Programul va folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea EXPRES.PAS, EXPRES.C sau EXPRES.CPP. Restricţii. 0 A, B , C , D 109 .
Rezolvare
Conform restricţiilor problemei, 0 A, B , C , D 109 . Prin urmare, pentru a evita erorile de depăşire, variabilele A, B, C, D şi Q trebuie declarate cu tipul de date longint. ExpresiiBooleene; Program { Clasele 07-09 } var Intrare, Iesire : text;
A, B, C, D, Q : longint; S : string;
5
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) if(A+B+C-D=Q) if(A+B-C+D=Q) if(A+B-C-D=Q) if(A-B+C+D=Q) if(A-B+C-D=Q) if(A-B-C+D=Q) if(A-B-C-D=Q)
then S:='+++'; then S:='++-'; then S:='+-+'; then S:='+--'; then S:='-++'; then S:='-+-'; then S:='--+'; then S:='---';
{ scrierea datelor de iesire } writeln(Iesire, S); close(Iesire); . end
6
Iepuraşii Iepuraşii
În cartea sa Liber Abaci (Cartea Abacului), editată în anul 1202, marele matematician Fibonacci (cunoscut şi sub numele Leonardo Pisano) a rezolvat „problema înmulţirii iepuraşilor”, care constă în calcularea numărului de iepuraşi într -o -o populaţie ce evoluează pornind de la o pereche iniţială. Conform acestei cărţi, dezvoltarea populaţiei de iepuraşi se descrie cu ajutorul şirului 1, 1, 2, 3, 5, 8, 13, ... , denumit şirul lui Fibonacci Fibon acci. Formal, acest şir se defineşte cu ajutorul următoarelor for mule: mule: F 1 1 , F 2 1 , F 3 2 , F 4 3 ,
..., F i 1 F i F i 1 , ... ,
unde F i reprezintă numărul de perechi de iepuraşi în luna i. Sarcină. Elaboraţi un program care calculează suma S a a primelor n numere din şirul lui
Fibonacci. Date de intrare. Fişierul text IEPURE.IN conţine pe o singură linie numărul
întreg n.
Date de ieşire. Fişierul text IEPURE.OUT va conţine pe singură linie numărul întreg S . Exemplu. IEPURE.IN 4
IEPURE.OUT 7
Timpul de execuţie nu va depăşi 0,5 secunde. Programul va folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea IEPURE.PAS, IEPURE.C sau IEPURE.CPP. Restricţii. 2 n 30 300 0.
Rezolvare
În general, problema nu prezintă dificultăţi de algoritmizare, întrucât suma S poate fi calculată printr -o singură parcurgere a şirului lui Fibonacci. Mai mult ca atât, nu este necesară nici memorarea întregului şir, întrucât numărul 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 poate fi calculată calculată cu ajutorul următoare următoareii secvenţe 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;
Implementând această secvenţă cu ajutorul declaraţiilor de variabile var S, F1, F2, F3 : integer;
7
prin experimente de calcul ne convingem, că începând cu n 20 , la calcularea valorilor variabilelor în studiu au loc erori de depăşire. Cei care cunosc alte tipuri de date, ar putea să încerce declaraţia 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 depăşire. Pentru a evita erorile de depăşire, vom reprezenta numerele mari S, F1, F2 şi F3 prin tablouri, formate din K elemente. Fiecare element al tabloului memorează câte o cifra a numărului respectiv. 1
2
...
3
K
Dacă în procesul adunării a două numere mari A şi B, formate din câte K cifre, transportul calculat în cazul însumării cifrelor A[1], B[1] şi transportului din rangul precedent diferă de zero, are loc o depăşire şi programul va semnaliza o eroare. În programul ce urmează, pentru a realiza experimentul de calcul, în componenţa procedurii Adunare a fost inclusă următoarea secvenţă de instrucţiuni: if Transport=1 then begin
writeln('Depasire'); readln; end ;
Evident, în condiţiile problemei, această secvenţă de instrucţiuni nu va fi executată nici o dată numai atunci, când 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 0 , aşa cum este indicat în restricţiile problemei. n 30 Program Iepurasii; 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;
8
begin
assign(Iesire, 'IEPURE.OUT'); rewrite(Iesire); j:=1; while S[j]=0 do j:=j+1; while jm 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 .
while n m , care, Numărul de iteraţii din instrucţiunea poatecomparabilă depăşi valoare conform restricţiilor problemei este de ordinul 10 10 , nu mărime cu capacitatea de prelucrare a calculatoarelor moderne. Prin urmare, timpul de calcul va fi mai mic decât 1 secundă.
16
Numere
Se consideră o mulţime finită, elementele căreia sînt numere naturale mai mici ca 32 700. Scrieţi un program, care selectează din această mulţime numerele prime. Date de intrare. Fişierul text NUMERE.IN conţine pe fiecare linie câte un număr natural.
Date de text ieşire. Fişierul NUMERE.OUT va conţine pe fiecare linie cuvîntul DA dacă numărul natural din linia respectivă a fişierului de intrare este un număr prim şi NU în caz contrar. Exemplu. NUMERE.IN 41 4 53
NUMERE.OUT DA NU DA
Restricţii.
Fişierul de intrare NUMERE.IN conţine cel mult 100 ddee linii. Timpul de execuţie nu va depăşi 0.5 secunde. Fişierul sursă va avea denumirea NUMERE.PAS, NUMERE.C sau NUMERE.CPP.
Rezolvare
Conform definiţiei, numărul i, i 2, este prim dacă el se împarte fără rest numai la 1 şi la el însuşi. Această definiţie poate fi aplicată direct împărţind numărul i la 2, 3, 4, ..., (i div 2). Evident, timpul cerut de un astfel de algoritm T i. Luînd în considerare faptul că valoarea maximă a numărului i este 32 700, iar fişierul de intrare conţine cel mult 100 de numere, timpul de execuţie T 32700 100 3 106. Intrucît capacitatea de prelucrare a calculatorului personal este de ordinul 109 instrucţiuni pe secundă, timpul de execuţie T va va fi mai mic ca 0,5 secunde.
Program Numere; 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 j)=0 then j)=0 if (i mod begin EsteNumarPrim:='NU';
17
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 .
18
Lupta maritimă maritimă
În cunoscutul joc „Lupta maritimă”, acţiunea are loc pe o fo aie de hârtie, liniată în pătrăţele, cu dimensiunile m rânduri şi n coloane. Pe câmpul de joc se pot afla corăbii distincte de formă arbitrară. Fiecare corabie reprezintă o figură conexă (figura se numeşte conexă dacă din orice pătrăţel al ei se poate ajunge la oricare alt pătrăţel din componenţa acesteia, deplasarea din pătrăţelul curent în unul din cele patru pătrăţele vecine efectuându-se prin latura comună). Oricare două corăbii nu au puncte comune şi nu se ating la colţuri (vezi desenul exemplu). În din procesul jocului, fiecare din corăbii se poate afla în una din următoarele trei stări: dacă în corabie nu s-a „nimerit” nici o dată, ea se consideră „vie”; dacă în fiecare din pătrăţelele din componenţa unei cor abii abii s-a „nimerit”, ea se consideră “ucisă”; în caz contrar corabia se consideră „rănită”. Sarcină.
Elaboraţi un program care calculează numărul corăbiilor „vii”, numărul corăbiilor „ucise” şi numărul corăbiilor „rănite”. Fişierul text LUPTA.IN conţine pe o singură linie numerele întregi m şi n, separate prin spaţiu. Fiecare din următoarele m linii ale fişierului de intrare conţine câte n numere întregi, separate prin spaţiu. Aceste numere pot lua următoarele valori: 0 –acest pătrăţel nu nu aparţine nici la o corabie (este apă); 1 – acest pătrăţel aparţine unei corăbii, dar în el nu s -a „nimerit”; -1 – acest pătrăţel aparţine unei corăbii şi în el s -a „nimerit”. Date de intrare.
Fişierul text LUPTA.OUT va conţine pe singură linie trei numere întregi, separate prin spaţiu: numărul de corăbii „vii” , numărul de corăbii „ucise” şi numărul de corăbii „rănite”. Date de ieşire.
Exemplu. LUPTA.IN 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
LUPTA.OUT 3 1 2
Timpul de execuţie nu va depăşi 0,1 secunde. Programul va folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea LUPTA.PAS, LUPTA.C sau LUPTA.CPP. Restricţii. 1 m , n 25 .
Rezolvare
Din analiza enun enunţului ţului problemei se oobservă bservă că operaţia operaţia de bază, ce trebuie efectuată de mai multe ori, constă în determinarea tuturor pătrăţele lor, care formează o corabie. Această operaţie poate fi făcută recursiv după cum urmează. x, y) aparţine unei corăbii. În procesul de analiză a Presupunem ( x pătrăţelului curent, că în pătrăţelul funcţie de curent valoarea respectivă, vom incrementa unul din contoarele k 1
19
(nu s-a nimerit) sau k 2 (s-a nimerit). Pentru a exclude pătrăţelul curent din analizele ulterioare, înscriem în el valoarea „0”. În continuare, vom analiza recursiv pătrăţelul de sus ( xx-1, y), cel din dreapta ( xx, y+1), cel din stânga ( x x, y-1) şi cel de jos ( x x+1, y). După parcurgerea tuturor pătrăţelelor din componenţa unei corăbii, în funcţie de valorile k 1 şi k 2, incrementăm unul din contoarele t 1 (vii), t 2 (ucise) sau t 3 (rănite). Pentru a evita verificările de la marginile câmpului de joc, în programul ce urmează el este încadrat în zerouri. LuptaMaritima; LuptaMaritima; Program { 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, readln(Intr are, 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
20
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]); ; { if } end end ; { Corabie } procedure NumaraCorabiile; 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 } ; { for } end end ; { NumaraCorabiile 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 rău caz, în procedura Corabie vor fi parcurse toate cel mn pătrăţele pătrăţele. Prin urmare, numărul de operaţii cerut d e program va fi de ordinul (mn) 2 . Evident, pentru m, n 25 , număr ul ul respectiv de operaţii va fi cu mult mai mic decât capacitatea de prelucrare a calculatoarelor calculatoarelor moderne moderne..
21
Clasele 10
Denumirea problemei
Vâslaşii
Iepuraşii
Segmente
Şarpele
Descompuneri
Radioul
Total
Numărul de puncte alocat problemei
12
Denumirea fişierului de intrare
Denumirea fişierului de ieşire
100
VASLASI.PAS VASLASI.C VASLASI.CPP
VASLASI.IN
VASLASI.OUT
100
IEPURE.PAS IEPURE.C IEPURE.CPP
IEPURE.IN
IEPURE.OUT
100
SEGMENT.PAS SEGMENT.C SEGMENT.CPP
SEGMENT.IN
SEGMENT.OUT
100
SARPE.PAS SARPE.C SARPE.CPP
SARPE.IN
SARPE.OUT
100
DESC.PAS DESC.C DESC.CPP
DESC.IN
DESC.OUT
100
RADIO.PAS RADIO.C RADIO.CPP
RADIO.IN
RADIO.OUT
600
Denumirea
fişierului sursă
-
22
-
-
Vâslaşii Vâslaşii
În sfârşit, în anul 2009, informaticienii au putut rezolva problema, care de mult titimp mp îi chinuia pe vâslaşii-sportivi. Se consideră o barcă lungă cu vâsle, în care sunt amplasaţi n vâslaşi. Fiecare vâslaş are câte o vâslă, cu care el vâsleşte doar pe o singură parte a bărcii − partea stânga sau partea dreapta. Evident, vâslind, fiecare sportiv comunică bărcii o accelerare nu doar strict în direcţia mişcării bărcii, bărcii, dar şi perpendicular pe ea. Apare întrebarea, cum trebuie să orientăm v âslele sportivilor din barcă, pentru ca ea să nu aibă oscilaţii, perpendiculare pe direcţia mişcării.
Din punct de vedere matematic, soluţia problemei se reduce la amplasarea semnelor „+” (vâsla pe partea stângă) şi „-” (vâsla pe partea dreaptă) în faţa numerelor de vâslaşi 1, 2, 3, ... n în aşa mod, încât suma obţinută să fie egală cu zero. De exemplu, pentru n 4 , avem 1 2 3 4 0 . Prin urmare, vâslaşii 1 şi 4 vor vâsli pe partea stânga, iar vâslaşii 2 şi 3 − pe partea dreapta dreapta a bărcii. Elaboraţi un program care amplasează semnele „+”, „ -” în faţa numerelor din şirul 1, 2, 3, ..., n în aşa mod, încât suma obţinută să fie egală cu zero. Sarcină.
Date de intrare. Fişierul text VASLASI.IN conţine pe o singură linie numărul
întreg n.
Date de ieşire. Fişierul
text VASLASI.OUT va conţine pe o singură linie un şir din n caractere, format din semnele +, -. Dacă problema are mai multe soluţii, în fişierul de ieşire se va scrie doar una, oricare din ele. Dacă problema nu are soluţii, în fişierul de ieşire se va scrie un şir din n caractere, format din semnul ?. Exemplul 1. VASLASI.IN 4
VASLASI.OUT +--+
Exemplul 2. VASLASI.IN 5
VASLASI.OUT ?????
Timpul de execuţie nu va depăşi 2,0 secunde. Programul va folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea VASLASI.PAS, VASLASI.C sau VASLASI.CPP. Restricţii. 2 n 21 .
Rezolvare
Problema poate fi rezolvată prin metoda trierii, examinând toate variantele posibile de amplasare a semnelor „+”, „-” în faţa numerelor din şirul 1, 2, 3, ..., n. i a a genera variantele de iamplasare, vom folosistângă numărul binarşiOrientare căruiaPentru ia valoarea 0 daca vâslaşul are vâsla pe partea a bărcii valoarea, 1cifra în caz contrar.
23
Iniţial, număr ul ul binar Orientare are valoarea 00...000. În continuare, la fiecare iteraţie vom aduna la acest număr valoarea binară “1”, operaţie cunoscută în informatică ca incrementare. În programul ce urmează, numărul binar Orientare este reprezentat prin tabloul cu acelaşi nume, iar operaţia de incrementare se realizează r ealizează cu ajutorul procedurii Increment. Vaslasii; Vaslasii; Program { 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]n;
Implementând această secvenţă cu ajutorul declaraţiilor de variabile var S, F1, F2, F3 : integer;
26
prin experimente de calcul ne convingem, că începând cu n 20 , la calcularea valorilor variabilelor în studiu au loc erori de depăşire. Cei care cunosc alte tipuri de date, ar putea să încerce declaraţia 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 depăşire. Pentru a evita erorile de depăşire, vom reprezenta numerele mari S, F1, F2 şi F3 prin tablouri, formate din K elemente. Fiecare element al tabloului memorează câte o cifra a numărului respectiv. 1
2
...
3
K
Dacă în procesul adunării a două numere mari A şi B, formate din câte K cifre, transportul calculat în cazul însumării cifrelor A[1], B[1] şi transportului din rangul precedent diferă de zero, are loc o depăşire şi programul va semnaliza o eroare. În programul ce urmează, pentru a realiza experimentul de calcul, în componenţa procedurii Adunare a fost inclusă următoarea secvenţă de instrucţiuni: if Transport=1 then begin
writeln('Depasire'); readln; end ;
Evident, în condiţiile problemei, această secvenţă de instrucţiuni nu va fi executată nici o dată numai atunci, când 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 0 , aşa cum este indicat în restricţiile problemei. n 30 Program Iepurasii; 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;
27
begin
assign(Iesire, 'IEPURE.OUT'); rewrite(Iesire); j:=1; while S[j]=0 do j:=j+1; while j 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); ; { ComparaPuncte ComparaPuncte } end 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 0 do right := right - 1; while ComparaPuncte(data[ if left 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 a > retelei 0 do pozitie := retele[pozitie].tata; z := retele[pozitie].tat pozitie; {salvam id-ul 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 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 boolean; rezultat, componenta : longint; f : text;
33
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 operaţiilor efectuate în cadrul Etapelor I şi II rezultă, că complexitatea 300 300 000 000 . temporală a programului este O(n log n) . Conform restricţiilor problemei, 3 n Prin urmare, numărul de operaţii va fi de ordinul 10 7 , mărime comparabilă cu capacitatea de prelucrare a calculatoarelor personale din laboratorul de informatică. Menţionăm, că numerele reale ce reprezintă coordonatele extremităţilor de segmente conţin cel mult trei cifre după virgulă. P utem folosi acest fapt pentru a trece de la coordonatele reale laîncele întregi,Citeste care, evident, sunt reale, procesate mai rapid. Anume, sunt din aceste considerente, procedura , numerele cititemult din fişierul de intrare înmulţite cu 1 000 şi transformate în numere întregi.
34
Şarpele Şarpele
Intr-o versiune simplificată a binecunoscu binecunoscutul tul joc clasic de calculator Snake (Şarpele), un şarpe trebuie sa mănânce toate merele dintr-o livada. Regulile acestui joc sunt foarte simple: 1. În livada sunt împrăştiate pe pământ n mere, poziţia fiecărui mar i fiind definită prin coordonatele carteziene întregi ( x xi, yi). 2. Originea sistemului de coordonate se află în colţul stânga -jos al livezii. Axa de coordonate 0X este orientată de la stânga la dreapta, iar axa 0Y – de de jos în sus. 3. La începutul jocului, în livada intra un şarpe, care are drept scop să mănânce toate merele din livada. 4. Poziţia curentă a şarpelui este definită prin coordonatele carteziene întregi ( xx s, y s). 5. Poziţia iniţială a şarpelui este (1, 1). 6. Repertoriul de comenzi ale şarpelui include instrucţiunile de deplasare SUS, JOS, STÂNGA, DREAPTA. Execuţia unei astfel de comenzi constă în deplasarea şarpelui în direcţia respectivă exact cu o unitate de lungime. 7. Atunci când coordonatele şarpelui devin egale cu coordonatele unui măr, şarpele mănâncă mărul respectiv. Scopul jocului constă în deplasarea şarpelui în aşa mod, încât toate merele să fie mâncate, iar drumul parcurs de şarpe să fie cât mai scurt. Dorin este împătimit de jocul Snake. El îl joaca de multă vreme şi a stabilit mai multe recorduri. Din păcate, pe consola lui de jocuri s-a defectat butonul comenzii JOS. În consecinţă, şarpele, indiferent de poziţia în care se afla, poate executa doar comenzile SUS, STÂNGA, DREAPTA. Dorin insă nu s-a descurajat şi consideră această defecţiune o nouă provocare. Întrucât Dorin a devenit foarte priceput la acest joc, el doreşte să stabilească un nou record. El nu se îndoieşte deloc de abilităţile lui de a direcţiona şarpele pe orice drum posibil, dar vrea să ştie lungimea celui mai scurt drum, deplasând deplasându-se u-se pe care şarpele şarpele ar mânca toate merele. Sarcină. Elaboraţi un program, care, cunoscând coordonatele celor n mere, calculează lungimea L a celui mai scurt drum, deplasându-se pe care şarpele şarpele ar mânca toate merele. Date de intrare.
Fişierul text
SARPE.IN conţine pe
prima linie numărul întreg n.
xi, yi, separate Fiecare din. Linia următoarele linii ale de fişierului intrare conţine numerele i. prin spaţiu i 1 a n fişierului intrare de conţine coordonatele măruluiîntregi Date de ieşire. Fişierul text SARPE.OUT va conţine pe singură linie numărul întreg L. Exemplu. SARPE.IN 5 2 2 5 3 7 3 8 4 4 6
SARPE.OUT 16
10 000 000 ; 1 xi , yi 10 000 Restricţii. 1 n 000 . Timpul de
execuţie nu va depăşi 0,1
secunde. Programul va folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea SARPE.PAS, SARPE.C sau SARPE.CPP.
35
Rezolvare
Pentru a reduce numărul de cazuri particulare, vom include în mulţimea merelor încă unul, aflat pe poziţia (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 puţin câte un măr. Evident, fiecare din aceste linii poate fi definită univoc prin coordonata y p, y p { y1 , y 2 , ..., y m } . Întrucît avem garantat un măr pe poziţia (1, 1), rezultă că y1 1 . Prin x vom nota abcisa celui mai din stânga, iar prin x − abcisa celui mai din dreapta măr de pe linia y p. Deoarece repertoriul de comenzi ale şarpelui nu mai conţine comanda JOS, odată ajuns pe o linie cu mere, şarpele trebuie să mănînce toate merele de pe această linie şi abia apoi să se deplaseze în sus. În aceste condiţii, lungimea celui mai scurt drum poate fi calculată prin metoda programării dinamice, pornind de la linia cea de mai jos şi terminând cu linia cea de mai
p
p
sus. Pentru fiecare linie, şarpele poate termina parcurgerea ei în una din două poziţii: la capătul stâng sau la capătul drept al liniei. Pentru a formaliza acest proces, introducem în studiu următoarele funcţii: L( y p ) − lungimea celui mai scurt drum, parcurgându -l pe care şarpele mănâncă toate merele de pe liniile de mai jos şi de pe linia curentă, rămânând în poziţia celui mai din stânga măr de pe linia curentă y y p; L( y p ) − lungimea celui mai scurt drum, parcurgându-l pe care şarpele mănâncă toate merele de pe liniile de mai jos şi de pe linia curentă, rămânând în poziţia celui mai din dreapta măr de pe linia curentă y p. Valorile acestor funcţii pot fi calculate după următoarele formule recurente: p L(1) 2 * x x p 1 ; L(1) x p 1 ;
L( y p ) ( y p y p 1 ) min[ L( y p 1 ) | x p x p 1 |, L ( y p 1 ) | x p x p 1 |] ( x p xp ) ; L( y p ) ( y p y p 1 ) min[ L( y p 1 ) | x p 1 x p |, L ( y p 1 ) | x p 1 x p |] ( x p xp ) .
Evident, lungimea celui mai scurt drum va fi: L min[ L ( ym ), L ( ym )] .
unde ym este coordonata celei mai de sus linii. Program Sarpele; Sarpele; { Clasele 10-12 } const MAXPOZ = 10000; n:longint; var
px, py, num, minx, maxx, costMinX, costMaxX :array[1..MAXPOZ] of longint;
36
costMin:longint; procedure Citeste; fin:text; var
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; 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 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)
37
+ dy + (maxx[y] - minx[y]); prevY := y; end ; costMin := min(costMinX[prevY], costMaxX[prevY]); end ; { CalculCosturiMin 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, x şi x este suficientă o singură parcurgere liniară a datelor de intrare, deci complexitatea acestei proceduri este O(n) . În procedura CalculCosturiMin, valorile
p
p
L( y p ) şi L( y p ) se calculează de m ori. Întrucât m n , complexitatea acestei proceduri va fi, de asemenea, O(n) . Conform restricţiilor din enunţul problemei, n 10 000 000 . Prin urmare,
numărul de operaţii cerut de algoritm va fi de ordinul 10 4, mărime cu mult mai mică decât capacitatea de prelucrare a calculatoarelor din laboratorul de informatică.
38
Descompuneri
Numim k-descompunere a numărului natural nenul N reprezentarea acestuia ca o sumă de exact k numere naturale nenule, scrise în ordine strict crescă toare. În general, în dependenţă de valorile N şi k , numărul N poate poate să nu aibă nici una, să aibă doar una sau să aibă mai multe k-descompuneri. Exemple:
99 = = 49 +este o 1-odescompunere a numărului 9; 9; 5 este 2 -descompuner 2descompunere e a numărului descompunere re a numărului 9; 9 = 3 + 6 este o altă 2-descompune 9 = 1 + 3 + 5 este o 3-descompunere a numărului 9; 9 = 1 + 2 + 6 este o altă 3-descompune descompunere re a numărului 9.
Contraexemple:
9 = 3 + 1 + 5 nu este o 3-descompunere a numărului 9, întrucât în scrierea respectivă termenii 3 şi 1 nu apar în ordine crescătoare; 9 = 1 + 1 + 7 nu este o 3-descompunere a numărului 9, întrucât în scrierea respectivă termenii 1 şi 1 nu apar în ordine strict crescătoare; 9 = 0 + 9 nu este o 2-descompunere a numărului 9, întrucât în scrierea respectivă apare numărul 0. Sarcină. Elaboraţi un program, care, cunoscând numerele naturale N şi k , calculează numărul T de de k -descompuneri -descompuneri posibile ale lui N . Deoarece numărul T poate poate fi foarte mare, în fişierul de ieşire se va scrie doar restul împărţirii lui T la la 1000000000 (un miliard). Date de intrare.
Fişierul text DESC.IN conţine pe singura linie numerele întregi N şi k ,
separate prin spaţiu. Date de ieşire. Fişierul text DESC.OUT va numărului întreg T la la 1000000000 (un miliard).
conţine pe singură linie restul împărţirii
Exemple: 1)
2) 3)
DESC.IN
DESC.OUT
12 3
7
DESC.IN
DESC.OUT
90 1
1
DESC.IN
DESC.OUT
300 9
382665027
Restricţii. 1 k N 700 700 . Timpul de execuţie nu va depăşi 0,5 secunde. Programul va
folosi cel mult 32 Megaocteţi de memorie operativă. Fişierul sursă va avea denumirea DESC.PAS, DESC.C sau DESC.CPP. Rezolvare
Problema poate fi rezolvată prin metoda programării dinamice. În acest scop, introducem în studiu funcţia t (k , a, b) . Această funcţie reprezintă numărul de k -descompuneri -descompuneri ale numărului a, care au ca termen minim pe b. Evident, t (1, a, b) 1 dacă a b şi t (1, a, b) 0 în caz contrar. În general, se poate demonstra că funcţia în studiu poate fi scrisă într -o formă recurentă:
39
a b
t (k , a, b)
t (k 1, a b, i) . i b 1
Prin urmare, numărul T poate fi calculat prin însumarea valorilor t (k , N , j ) pentru j 1 , 2, ..., N : N
N N j
j 1
j 1 i j 1
T t (k , N , j )
t (k 1, N j, i) .
Complexitatea temporală a unui algoritm bazat pe formula recurentă de mai sus este O(kN 3 ) . În cazul restricţiilor problemei, numărul de operaţii cerut de algorit m va fi de ordinul 10 14 , mărime cu mult mai mare decât capacitatea de prelucrare a calculatoarelor personale din laboratorul de informatică informatică.. Pentru a reduce complexitatea temporală a algoritmului vom lua în considerare faptul că pentru valorile lui i a b funcţia t (k , a , b) 0 . În consecinţă, vom calcula doar sumele din “dreapta” indicelui respectiv. În acest caz complexitatea algoritmului va fi O( N 2 N ) , iar numărul cerut de operaţii va fi de ordinul 10 8 , mărime comparabilă cu capacitatea de
prelucrare a calculatoarelor calculatoarelor din labo laboratorul ratorul de informatica. Menţionăm faptul, că în procesul de efectuare a calculelor nu se cere memorarea tuturor valorilor t (k , a, b) , fiind suficiente doar valorile iteraţiei curente şi termenii din „dreapta”, în total circa 4 Megaocteţi 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: scrieRezultat(q: longint); { Scrie rezultatul în fişierul de ieşire ş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, fillchar(a , sizeof(a), 0); fillchar(sumaLaDreapta, sizeof(sumaLaDreapta), 0); assign(f, 'DESC.IN'); reset(f); readln(f, n, m);
40
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 calcularea t[k, x, y] pentru k > 1 } for i := 2 to m do begin { calculăm 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; { calculăm 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 } existaElementNenul then scrieRezultat(0); if not existaElementNenul end ; { calculam raspunsul } res := 0; for k := 1 to n do begin inc(res, a[n, k]); res := res mod MODULO; MODULO; end; scrieRezultat(res); end .
41
Radioul
Postul de radio “Bitul Liber” are la dispoziţie n emiţătoare, amplasate în n localităţi distincte. Poziţia fiecărui emiţător i, i 1, 2, 3, ..., n , este definită prin coordonatele carteziene întregi ( xi , yi ) . Fiecare emiţător are una şi aceiaşi putere de emisie R. Evident, cu cât emiţătorul este mai puternic, cu atât este mai mare şi distanţa la care se propagă undele emise de el. În scopuri didactice, se consideră, că undele unui emiţător de puterea R se propaga până la distanţa de R kilometri. Deşi emiţătoarele au una şi aceiaşi putere R, fiecare din ele poate fi acordat în mod individual să emită pe una din frecvenţ ele disponibile frecvenţa F 1 sau frecvenţa F 2. Este cunoscut faptul, că undele radio interferează şi, în consecinţă, dacă un receptor este supus acţiunii concomitente concomitente a două unde de aceiaşi frecvenţă, frecvenţă, calitatea recepţiei scade brus brusc. c. În general, conform regulilor de radiodifuziune, calitatea recepţiei se consideră bună doar atunci, când nu există nici un sector de arie nenulă, pe care ajung unde radio, emise de două emiţătoare ce au aceiaşi frecvenţă de emisie. Este evident faptul, că pentru a extinde aria pe care pot fi recepţionate emisiunile postului de radio “Bitul Liber”, puterea de emisie R trebuie mărită. Concomitent, frecvenţele de emisie F 1, F 2 pentru fiecare din emiţătoare trebuie alese în aşa mod, încât să se asigure calitatea recepţiei emisiunilor respective. Sarcină . Scrieţi un program, care, cunoscând coordonatele calculeazăa R, ce mai puterea ma ximală de emisie permite încă alegerea pentruemiţătoarelor, fiecare din emiţătoare unor astfel de frecvenţe, încât se asigură calitatea recepţiei. r ecepţiei. Date de intrare. Fişierul text RADIO.IN conţine pe prima linie numărul întreg n. Fiecare din următoarele n linii ale fişierului de intrare conţine numerele întregi xxi, yi, separate prin spaţiu. Linia i 1 a fişierului de intrare conţine coordonatele emiţătorului i. Date de ieşire. Fişierul text RADIO.OUT va conţine pe pe prima linie numărul real R, scris -8 cu o precizie nu mai mică de 10 . Linia a doua a fişierului de ieşire va conţine n numere întregi, separate prin spaţiu. Numărul care apare pe locul i de pe această linie va fi egal cu 1, dacă emiţătorul i trebuie să emită pe frecvenţa F 1, şi egal cu 2, dacă emiţătorul respectiv trebuie să emită pe frecvenţa F 2. Dacă problema admite mai multe soluţii, în fişierul de ieşire se va scrie doar una din ele. Exemplu. RADIO.IN 4 0 0 0 1 1 0 1 1
RADIO.OUT 0.70710678118654752 1 2 2 1
Timpul de execuţie nu va depăşi 2,0 secunde. Programul va folosi cel mult 64 Megaocteţi de memorie operativă Fişierul sursă va avea denumirea RADIO.PAS, RADIO.C sau RADIO.CPP. 1 200 200 ; 10 4 x , yi 10 4 . Restricţii. 3 n
Rezolvare
Din enunţul problemei rezultă că puterea maximală nu poate depăşi jumătate din distanţa dintre emiţătoarele aflate la cea mai mare distanţă unul de altul. Prin urmare, valoarea
42
concretă a lui R trebuie căutată în intervalul [0, Lmax ] , unde Lmax poate fi calculat conform unor formule evidente: xmin min( x1 , x2 , ..., xn )
;
ymin min( y1 , y2 , ..., yn )
;
xmax max( x1 , x2 , ..., xn )
;
ymax max( y1 , y2 , ..., yn ) . Lmax
( xmax xmin ) 2 ( y max y min ) 2
2
1.
Presupunem, că avem la dispoziţie o funcţie Test ( R) , care ia valoarea 1, dacă pentru puterea de emisie R putem stabili frecvenţele emiţătoarelor în aşa mod, încât să garantăm calitatea recepţiei, şi 0 în caz contrar. Evident, având o astfel de funcţie, putem calcula puterea maximală prin metoda înjumătăţirii, pornind de la intervalul iniţial [0, Lmax ] şi R Lmax / 2 . Pentru a construi funcţia Test ( R) , reunim cu câte o linie emi ţătoarele, distanţa dintre care este strict mai mică de 2 R . Evident, oricare două emiţătoare, ce sunt reunite printr -o -o astfel de linie, trebuie să aibă frecvenţe de emisie diferite. În continuare, vom încerca să partiţionăm mulţimea emiţătoarelor în două submulţimi, notate prin A şi B . În cadrul fiecărei submulţimi, emiţătoarele respective nu trebuie să fie reunite între ele. Dacă acest lucru ne reuşeşte, funcţia Test ( R) va lua valoarea 1. În caz contrar, adică a rămas cel puţin un emiţător, ce nu po ate fi incluse nici în submulţimea A , nici în submulţimea B , funcţia Test ( R) va lua valoarea 0.
Test ( R ) 1
Test ( R ) 0
Formarea submulţimilor A şi B poate poate fi simulată prin „vopsirea” „vopsirea” fiecă fiecărui rui emiţător în una din cele două culori: albă sau neagră. Iniţial, vopsim un emiţător arbitrar în culoare albă sau, prin alte cuvinte, îl includem în submulţimea A . În continuare, determinăm toate emiţătoarele, reunite cu emiţătorul proaspăt vopsit, şi le vopsim în culoarea neagră sau, prin alte cuvinte, le includem în submulţimea B . În general, la fiecare pas al algoritmului, ce încearcă vopsirea Greedy, vecinilor emiţătorului curent în culoarea „opusă” complexitatea unui astfel de algoritm este O (n 2 ) . . În cazul tehnicii de programare
43
Evident, după determinarea valorii maximale R, pentru care funcţia Test ( R) mai ea încă valoarea 1, tuturor emiţătoarelor din mulţimea A li se atribuie una din frecvenţe, de exemplu, F 1, iar celor din submulţimea B cealaltă frecvenţă, de exemplu, frecvenţa F 2. Program Radio; 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; begin while j 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 .
View more...
Comments