Mai sunt vreo două luni până la examenul de admitere la Facultatea de Informatică din Iași, așa că m-am gândit să adaug și rezolvările subiectelor date în anul 2017. Pe cele din 2018 le puteți găsi aici.

Subiectul I. Problema 1.

Fie x o variabilă întreagă care conține cel mai mic număr natural nenul, multiplu de 36, divizibil cu toate numerele prime mai mici decât 10. Precizați care dintre expresiile C++ de mai jos este adevărată.

  • A. (x < 1000) && (x % 27 == 0)
  • B. (x > 1000) && ((x * x * x) % 1000 == 0)
  • C. ((x * x) / 16) % 2 == 0
  • D. (x % 100 == 0) || (x / 100 == 0)

Numerele prime mai mici decât 10 sunt 2, 3, 5, 7, deci variabila x este egală cu cmmmc(2, 3, 5, 7, 36) = 1260. Acum trebuie să evaluăm fiecare expresie dată în parte. A este falsă deoarece 1260 > 1000. B este adevărată pentru că 1260 > 1000 și x * x * x este divizibil cu 1000, căci se termină în trei zerouri. C este falsă deoarece (x * x) / 16 == 99225, care este impar. D este falsă pentru că nici x % 100 == 0 și nici x / 100 == 0 nu sunt adevărate. Așadar, varianta corectă este B.

Subiectul I. Problema 2.

Se consideră subprogramul F de mai jos, descris în pseudocod. Subprogramul primește un număr natural nenul în parametrul u și întoarce un număr natural când se oprește.

a) Care este valoarea returnată de subprogram pentru parametrul u = 10?

Iată valorile variabilelor u și count inițiale și după fiecare intrare în while:

Răspunsul este 6.

b) Dați exemplu de un număr natural u astfel încât F(u) să returneze 7.

Trebuie să găsim un număr pentru care se efectuează 7 operații. Cel mai simplu ar fi să-l facem să treacă mereu prin prima ramură a if-ului, pentru a-i afla ușor valoarea. Observăm că F(2n) returnează n, deoarece 2n va trebui să se înjumătățească de n ori pentru a deveni 1. Deci, un răspuns posibil este 27, adică 128.

c) Scrieți în pseudocod un subprogram recursiv, echivalent cu F, care nu folosește instrucțiuni repetitive.

Privim fiecare intrare în while a lui u ca pe un apel recursiv ce îl are ca parametru pe noul u. Subprogramul recursiv cerut va arăta cam așa:

d) Scrieți o funcție C++ care implementează subprogramul F dat.

Un răspuns posibil este:

Subiectul II. Problema 1.

O matrice cu 8 linii, formată doar din 0 și 1, are următoarele trei proprietăți:

  • i. Prima linie conține un singur element cu valoarea 1.
  • ii. Linia j conține de două ori mai multe elemente nenule decât linia j - 1, pentru orice j ∈ {2, 3, …, 8}.
  • iii. Ultima linie conține un singur element cu valoarea 0.

Care este numărul total de elemente cu valoarea 0 din matrice?

  • A. 777
  • B. 769
  • C. 528
  • D. Nu există o astfel de matrice

Din primele două proprietăți, rezultă că linia j are 2j - 1 elemente nenule. Cum ultima linie conține un singur element cu valoarea 0, deducem că numărul de coloane ale matricei este 27 + 1 = 129 (numărul de elemente de pe ultima linie). Putem calcula numărul de zerouri din matrice scăzând numărul de elemente nenule din numărul total de elemente. Obținem:

$$8 \cdot 129 - (2^0 + 2^1 + \cdots + 2^7) = 1032 - (2^8 - 1) = 777$$

Deci, răspunsul corect este A.

Subiectul II. Problema 2.

Fie T un arbore și v un nod al acestuia. Construim un graf G astfel: creăm 11 copii distincte T1, T2, …, T11 ale arborelui T și adăugăm toate muchiile posibile între nodurile v1, v2, …, v11, unde vi este copia din arborele Ti corespunzătoare nodului ales v. În graful rezultat, numărul de muchii este dublul numărului de noduri. Câte noduri are arborele inițial T?

  • A. 6
  • B. 5
  • C. 4
  • D. Niciuna dintre variantele A, B, C

Notăm cu x numărul de noduri din arborele inițial T. Nodurile v1, v2, …, v11 vor forma un subgraf complet (o clică), deci contribuie cu 11 * 10 / 2 = 55 de muchii la graful final. La acestea se adaugă muchiile celor 11 arbori, deci 11 * (x - 1) muchii. (Se știe că un arbore cu x noduri are x - 1 muchii.) Numărul de noduri ale grafului este 11 * x. Știind că graful rezultat are de două ori mai multe muchii decât noduri, obținem: 55 + 11 * (x - 1) = 22 * x, de unde x = 4. Varianta corectă este C.

Subiectul II. Problema 3.

Considerăm un alfabet A format dintr-o mulțime finită de caractere. Un cuvânt este o secvență nevidă de caractere distincte din A. Lungimea unui cuvânt este numărul de caractere din care acesta este format. Un p-sistem (p ≥ 1) este definit ca o mulțime S de cuvinte, toate de lungime p, având proprietățile:

  • P1. Oricare două cuvinte din S au exact un caracter comun.
  • P2. Orice caracter din alfabetul A apare în cel puțin un cuvânt din S.

Exemplu: Pentru A = {'a', 'b', 'c'}, mulțimea S = {"ab", "ac"} este un 2-sistem.

a) Scrieți un 3-sistem peste alfabetul A = {'a', 'b', 'c', 'd', 'e', 'f'}.

3-sistem peste A

Glumesc.

S = {"abc", "ade", "bdf"}

b) Scrieți un program C++ care:

  • b1) Citește de la tastatură un întreg m, 1 ≤ m ≤ 26, și construiește alfabetul A format din primele m caractere ale alfabetului {'a', 'b', …, 'z'}.
  • b2) Citește de la tastatură un întreg n ≥ 1 și o secvență de n șiruri de caractere. Se presupune că fiecare șir citit este format din caractere distincte din A (este un cuvânt). Nu este necesară validarea datelor citite.
  • b3) Verifică dacă există un p astfel încât mulțimea de cuvinte citite reprezintă un p-sistem. În caz afirmativ, va fi afișat mesajul "DA", în caz contrar "NU".

Ca să ne ușurăm munca, am folosit operații pe biți. Recomand citirea acestui articol înainte de a continua. Pentru reprezentarea cuvintelor și a „vectorilor caracteristici” am reținut bitmask-uri în care bitul de pe poziția i este 1 dacă a i-a literă din alfabet este inclusă în acea submulțime (numerotarea făcându-se de la 0). Probabil că se acorda punctaj maxim și pentru un program fără operații pe biți, dar soluția aceea e prea directă și plictisitoare. Și mai puțin eficientă.

Pentru prima cerință, am reținut alfabetul într-un bitmask cu 1 pe biții 0, 1, …, m - 1. Acest mod de reprezentare ne va ajuta mai încolo. Pentru a doua cerință, am definit un struct String (pentru reținerea cuvintelor) cu două câmpuri: len (lungimea) și mask (bitmask cu literele incluse în cuvânt). Am reținut cuvintele într-un vector s cu elemente de tip String.

Pentru a treia cerință, am parcurs vectorul s verificând dacă lungimile tuturor cuvintelor sunt egale (condiție necesară pentru a forma un p-sistem). În același timp, am reținut într-un bitmask mask literele care apar în cel puțin un cuvânt. Acest număr se calculează efectuând sau pe biți între toate bitmask-urile cuvintelor. Înainte de a continua, verificăm dacă toate literele din alfabet au fost găsite, adică dacă mask == a. La final, pentru fiecare două cuvinte din s, testăm dacă intersecția lor (x.mask & y.mask) are un singur element. Asta se face ușor cu operații pe biți, testând dacă numărul respectiv este o putere a lui 2.

Subiectul II. Problema 4.

O rețea de comparatori de tip n (n ≥ 2) și de dimensiune m (m ≥ 1) este o secvență (c1, c2, …, cm) în care fiecare element ci, numit comparator, este o pereche de numere întregi (j, k) cu proprietatea 1 ≤ j < k ≤ n.

Exemplu: Rețeaua R = ((1, 2), (2, 3)) are tipul 3 și dimensiunea 2.

Dacă a este un vector de n numere întregi și R este o rețea, notăm cu R(a) vectorul obținut aplicând următoarele transformări lui a: Pentru fiecare comparator ci = (j, k), 1 ≤ i ≤ m, din R, în ordinea în care aceștia apar în rețea, dacă a[j] > a[k] atunci în vectorul a interschimbăm valorile de la pozițiile j și k.

Exemplu: Pentru R = ((1, 2), (2, 3)) și a = (30, 20, 10) avem R(a) = (20, 10, 30).

a) Fie R = ((2, 4), (1, 2), (3, 4), (2, 3)) și a = (40, 30, 20, 10). Calculați R(a) și scrieți valorile intermediare ale vectorului a corespunzătoare transformărilor efectuate.

b) Dați exemplu de o rețea R de tip 4 cu proprietatea că pentru orice vector a format din 4 numere întregi distincte, R(a) va avea elementele ordonate crescător. Justificare.

R = ((1, 2), (2, 3), (3, 4), (1, 2), (2, 3), (1, 2)). Această rețea de comparatori este practic o simulare a algoritmului Bubble Sort: După primele 3 operații, elementul cel mai mare ajunge pe ultima poziție, după următoarele 2 operații, al doilea cel mai mare element ajunge pe poziția 3, iar la final și celelalte două elemente vor ajunge pe pozițiile corespunzătoare.

c) Scrieți o funcție C++ care primește ca parametri numerele naturale n și m, o matrice R cu m linii și 2 coloane, reprezentând o rețea de comparatori, și un vector a de n numere întregi. Funcția va calcula vectorul R(a) și va returna valoarea 1 dacă acesta are elementele ordonate crescător. În caz contrar, va returna valoarea 0.

Din nou, se cere scrierea un program destul de imediat. Pur și simplu efectuăm operațiile, iar la final parcurgem vectorul pentru a testa dacă este sortat.

Subiectul III. Problema 1.

Se consideră toate șirurile de lungime l ∈ {1, 2, 3} formate din litere din mulțimea {'a', 'b', 'c', 'd', 'e'}. Câte dintre aceste șiruri au un număr par de vocale? ('a' și 'e' sunt vocale)

  • A. 79
  • B. 80
  • C. 81
  • D. 78

Trebuie avută atenție că literele dintr-un cuvânt se pot repeta. Șirurile de lungime 1 trebuie să conțină o consoană, deci numărul lor este 3. Șirurile de lungime 2 trebuie să conțină fie două consoane (32 variante), fie două vocale (22 variante). Șirurile de lungime 3 pot conține trei consoane (33 variante), sau o consoană și două vocale (3 * 31 * 22 variante). Explicația pentru 3-ul din ultima formulă este că putem alege în 3 moduri poziția unde punem consoana. Dacă avem fixată această poziție, știm că pe celelalte vom pune vocale, așa că nu mai înmulțim formula cu nimic. Adunând rezultatele obținem 79, deci răspunsul este A.

Subiectul III. Problema 2.

Se consideră funcția recursivă F de mai jos. Ce valoare va returna apelul F(0, 63, 64)?

Trebuie doar să urmărim lanțul de apeluri recursive. Se observă că funcția dată calculează radicalul lui t în timp logaritmic, folosind o tehnică asemănătoare căutării binare. Precizia este însă foarte proastă, fiind vorba de numere întregi.

Subiectul III. Problema 3.

O casă are n × m camere (n, m ≥ 2) și este reprezentată ca o matrice cu n linii (numerotate de la 0 la n - 1) și m coloane (numerotate de la 0 la m - 1). Camera de la linia i (0 ≤ i ≤ n - 1) și coloana j (0 ≤ j ≤ m - 1) este identificată prin perechea de numere (i, j). Toate camerele, cu excepția celor situate pe prima linie (i = 0) și a celor situate pe prima coloană (j = 0), au câte un comutator. Acționarea comutatorului din camera (i, j) (unde 1 ≤ i ≤ n - 1, 1 ≤ j ≤ m - 1) conduce la următorul rezultat: În fiecare dintre camerele (i, j), (i - 1, j), (i, j - 1), (i - 1, j - 1) (camera cu comutatorul, cea de deasupra, cea de la stânga și cea de deasupra și la stânga), dacă lumina era aprinsă, ea se stinge, iar dacă era stinsă, se aprinde.

a) Matricea de mai jos reprezintă starea luminilor din fiecare cameră dintr-o casă cu n = 5 linii și m = 4 coloane. Pozițiile (0, 1), (0, 2), (1, 1), (1, 3), (2, 2), (2, 3) conțin valoarea 1, reprezentând faptul că luminile sunt aprinse în camerele respective, iar celelalte poziții conțin valoarea 0, reprezentând faptul că luminile sunt stinse. Comutatoarele din care camere trebuie acționate pentru a stinge luminile din toate camerele?

$$\begin{pmatrix}
0 & 1 & 1 & 0\\
0 & 1 & 0 & 1\\
0 & 0 & 1 & 1\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
\end{pmatrix}$$

Acționăm comutatoarele din camerele (1, 2) și (2, 3):

$$\begin{pmatrix}
0 & 1 & 1 & 0\\
0 & 1 & 0 & 1\\
0 & 0 & 1 & 1\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
\end{pmatrix} \to
\begin{pmatrix}
0 & 0 & 0 & 0\\
0 & 0 & 1 & 1\\
0 & 0 & 1 & 1\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
\end{pmatrix} \to
\begin{pmatrix}
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
\end{pmatrix}$$

b) Scrieți o funcție C++ care primește 5 parametri de intrare: o matrice A de 0 și de 1, reprezentând starea luminilor din camere, dimensiunile n (n ≥ 2) și m (m ≥ 2) ale acesteia, precum și două numere naturale i, j (1 ≤ i ≤ n - 1, 1 ≤ j ≤ m - 1), reprezentând o cameră (i, j) cu comutator. Funcția trebuie să modifice matricea primită ca parametru, astfel încât aceasta să conțină starea luminilor după acționarea comutatorului din camera (i, j). Nu este necesară validarea parametrilor de intrare.

Expresia x ^= true, unde x este de tip bool, îl schimbă pe x din 0 în 1, sau din 1 în 0. Desigur, în loc de x ^= true, puteam scrie x = !x, dar e mai drăguț cu XOR 😛

c) Scrieți o funcție C++ care primește ca parametri de intrare o matrice A de 0 și de 1, reprezentând starea luminilor din camere, precum și dimensiunile n (n ≥ 2) și m (m ≥ 2) ale acesteia. Funcția trebuie să returneze 1 dacă există o mulțime de camere astfel încât, acționând comutatorul în fiecare cameră din mulțime o singură dată, să se obțină stingerea luminilor din toate camerele. Altfel, funcția va returna 0. În rezolvare, puteți utiliza funcția de la punctul b). Nu este necesară validarea parametrilor de intrare. Se va folosi un algoritm cât mai eficient din punct de vedere al timpului de execuție; soluțiile de tip backtracking vor primi cel mult două puncte.

Observăm că nu contează ordinea în care acționăm comutatoarele, așa că vom alege una convenabilă: Parcurgem matricea de sus în jos și de la stânga la dreapta, începând de la linia 1 și coloana 1. Când ne aflăm la poziția (i, j), acționăm comutatorul doar dacă poziția (i - 1, j - 1) este setată la 1. Asta pentru că acea poziție nu va mai fi vizitată niciodată, și nu are sens să o facem 0 mai devreme, stricând eventual alte poziții deja vizitate.

Dacă poziția curentă (i, j) se află pe ultima linie și poziția (i, j - 1) a rămas setată la 1, înseamnă că nu există soluție, căci nu ne vom mai putea atinge de poziția respectivă. Similar, dacă suntem pe ultima coloană și poziția (i - 1, j) a rămas 1, nu există soluție. Când ne aflăm la ultima poziție, adică (n - 1, m - 1), testăm și dacă elementul de pe (n - 1, m - 1) a rămas 1, caz în care din nou nu avem soluție. Dacă matricea a trecut cu succes de toate aceste teste, înseamnă că există soluție, și vom returna true.

Complexitatea este $O(m \cdot n)$, și este optimă pentru că e egală cu dimensiunea input-ului. La început i-am făcut o copie matricei, și am lucrat pe ea, ca să nu modific matricea dată.

Sfârșit! Dacă aveți vreo întrebare despre problemele din acest articol, sau vreți să postez și rezolvările subiectelor date în alți ani, nu ezitați să lăsați un comentariu mai jos 🙂

Îți place conținutul acestui site?

Dacă vrei să mă susții în întreținerea server-ului și în a scrie mai multe articole de calitate pe acest blog, mă poți ajuta printr-o mică donație. Află aici cum o poți face!