Admitere Informatică Iași 2017 – Subiecte și rezolvări
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 o variabilă întreagă care conține cel mai mic număr natural nenul, multiplu de divizibil cu toate numerele prime mai mici decât Precizați care dintre expresiile C++ de mai jos este adevărată.
(x < 1000) && (x % 27 == 0)
(x > 1000) && ((x * x * x) % 1000 == 0)
((x * x) / 16) % 2 == 0
(x % 100 == 0) || (x / 100 == 0)
Numerele prime mai mici decât sunt și deci variabila este egală cu 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 de mai jos, descris în pseudocod. Subprogramul primește un număr natural nenul în parametrul și întoarce un număr natural când se oprește.
subprogram F(u)(u - număr natural nenul)count <- 0cât timp u != 1dacă u este par atunciu <- u / 2altfelu <- u * 3 + 1count <- count + 1returnează counta) Care este valoarea returnată de subprogram pentru parametrul
Iată valorile variabilelor și inițiale și după fiecare intrare în while
:
Răspunsul este
b) Dați exemplu de un număr natural astfel încât să returneze
Trebuie să găsim un număr pentru care se efectuează 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ă returnează deoarece va trebui să se înjumătățească de ori pentru a deveni Deci, un răspuns posibil este adică
c) Scrieți în pseudocod un subprogram recursiv, echivalent cu 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:
subprogram F(u) (u - număr natural nenul) dacă u = 1 atunci returnează 0 dacă u este par atunci returnează 1 + F(u / 2) returnează 1 + F(u * 3 + 1)
d) Scrieți o funcție C++ care implementează subprogramul dat.
Un răspuns posibil este:
int F(int u) { int count = 0; while (u != 1) { if (u % 2 == 0) u /= 2; else u = u * 3 + 1; count++; } return count;}
Subiectul II. Problema 1.
O matrice cu linii, formată doar din și are următoarele trei proprietăți:
- Prima linie conține un singur element cu valoarea
- Linia conține de două ori mai multe elemente nenule decât linia pentru orice
- Ultima linie conține un singur element cu valoarea
Care este numărul total de elemente cu valoarea din matrice?
- Nu există o astfel de matrice
Din primele două proprietăți, rezultă că linia are elemente nenule. Cum ultima linie conține un singur element cu valoarea deducem că numărul de coloane ale matricei este (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:
Deci, răspunsul corect este A.
Subiectul II. Problema 2.
Fie un arbore și un nod al acestuia. Construim un graf astfel: creăm copii distincte ale arborelui și adăugăm toate muchiile posibile între nodurile unde este copia din arborele corespunzătoare nodului ales În graful rezultat, numărul de muchii este dublul numărului de noduri. Câte noduri are arborele inițial
- Niciuna dintre variantele A, B, C
Notăm cu numărul de noduri din arborele inițial Nodurile vor forma un subgraf complet (o clică), deci contribuie cu de muchii la graful final. La acestea se adaugă muchiile celor arbori, deci muchii. (Se știe că un arbore cu noduri are muchii.) Numărul de noduri ale grafului este Știind că graful rezultat are de două ori mai multe muchii decât noduri, obținem: de unde Varianta corectă este C.
Subiectul II. Problema 3.
Considerăm un alfabet format dintr-o mulțime finită de caractere. Un cuvânt este o secvență nevidă de caractere distincte din Lungimea unui cuvânt este numărul de caractere din care acesta este format. Un sistem este definit ca o mulțime de cuvinte, toate de lungime având proprietățile:
- P1. Oricare două cuvinte din au exact un caracter comun.
- P2. Orice caracter din alfabetul apare în cel puțin un cuvânt din
Exemplu: Pentru mulțimea este un sistem.
a) Scrieți un sistem peste alfabetul
Glumesc.
b) Scrieți un program C++ care:
- b1) Citește de la tastatură un întreg și construiește alfabetul format din primele caractere ale alfabetului
- b2) Citește de la tastatură un întreg și o secvență de șiruri de caractere. Se presupune că fiecare șir citit este format din caractere distincte din (este un cuvânt). Nu este necesară validarea datelor citite.
- b3) Verifică dacă există un astfel încât mulțimea de cuvinte citite reprezintă un 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 este dacă a a literă din alfabet este inclusă în acea submulțime (numerotarea făcându-se de la 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 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
.
#include <cstring>#include <iostream>
using namespace std;
const int SMAX = 27; // lungimea maximă a unui șir este 26 + 1 de la '\0'const int NMAX = 100; // (NMAX - 1) = valoarea maximă pe care o poate lua n
// Un struct pentru reținerea cuvintelor:struct String { int len; // lungimea cuvântului int mask; // submulțimea de litere din cuvânt};
int n, m, a; // n, m, alfabetulString s[NMAX]; // vectorul de cuvinte
// O funcție care calculează mask-ul pentru un șir dat:int getMask(char* str) { int mask = 0; // Inițial nu avem nicio literă în cuvânt. for (int i = 0; str[i]; i++) // Parcurgem șirul. mask |= 1 << (str[i] - 'a'); // Setăm bitul literei str[i] la 1. return mask;}
// O funcție care testează dacă x este o putere a lui 2,// adică dacă x are exact un bit 1.bool isPowOf2(int x) { // Considerăm că x este nenul. return !(x & (x - 1));}
int main() { char str[SMAX];
// Cerința b1: cin >> m; a = (1 << m) - 1; // Construim alfabetul.
// Cerința b2: cin >> n; for (int i = 1; i <= n; i++) { cin >> str; s[i].len = strlen(str); s[i].mask = getMask(str); }
// Cerința b3: int mask = s[1].mask; // mask reține submulțimea literelor care se regăsesc în cel puțin un cuvânt: for (int i = 2; i <= n; i++) { mask |= s[i].mask; // Actualizăm mask cu mask-ul curent. if (s[i].len != s[1].len) { // Testăm dacă s[i] (nu) are aceeași lungime ca șirurile precedente. cout << "NU\n"; return 0; } }
if (mask != a) { // Testăm dacă mask nu cuprinde toate literele din a. cout << "NU\n"; return 0; }
// Luăm câte două cuvinte for (int i = 1; i < n; i++) for (int j = i + 1; j <= n; j++) if (!isPowOf2(s[i].mask & s[j].mask)) { // și testăm dacă (nu) au exact o literă în comun. cout << "NU\n"; return 0; }
cout << "DA\n"; return 0;}
Subiectul II. Problema 4.
O rețea de comparatori de tip și de dimensiune este o secvență în care fiecare element numit comparator, este o pereche de numere întregi cu proprietatea
Exemplu: Rețeaua are tipul și dimensiunea
Dacă este un vector de numere întregi și este o rețea, notăm cu vectorul obținut aplicând următoarele transformări lui Pentru fiecare comparator din în ordinea în care aceștia apar în rețea, dacă atunci în vectorul interschimbăm valorile de la pozițiile și
Exemplu: Pentru și avem
a) Fie și Calculați și scrieți valorile intermediare ale vectorului corespunzătoare transformărilor efectuate.
b) Dați exemplu de o rețea de tip cu proprietatea că pentru orice vector format din numere întregi distincte, va avea elementele ordonate crescător. Justificare.
Această rețea de comparatori este practic o simulare a algoritmului Bubble Sort: După primele operații, elementul cel mai mare ajunge pe ultima poziție, după următoarele operații, al doilea cel mai mare element ajunge pe poziția 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 și o matrice cu linii și coloane, reprezentând o rețea de comparatori, și un vector de numere întregi. Funcția va calcula vectorul și va returna valoarea dacă acesta are elementele ordonate crescător. În caz contrar, va returna valoarea
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.
bool R(int n, int m, int r[][2], int a[]) { // Efectuăm operațiile: for (int i = 1; i <= m; i++) if (a[r[i][0]] > a[r[i][1]]) { int aux = a[r[i][0]]; a[r[i][0]] = a[r[i][1]]; a[r[i][1]] = aux; }
// Testăm dacă a este sortat: for (int i = 1; i < n; i++) if (a[i] > a[i + 1]) return false; return true;}
Subiectul III. Problema 1.
Se consideră toate șirurile de lungime formate din litere din mulțimea Câte dintre aceste șiruri au un număr par de vocale? și sunt vocale)
Trebuie avută atenție că literele dintr-un cuvânt se pot repeta. Șirurile de lungime trebuie să conțină o consoană, deci numărul lor este Șirurile de lungime trebuie să conțină fie două consoane variante), fie două vocale variante). Șirurile de lungime pot conține trei consoane variante), sau o consoană și două vocale variante). Explicația pentru ul din ultima formulă este că putem alege în 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 deci răspunsul este A.
Subiectul III. Problema 2.
Se consideră funcția recursivă de mai jos. Ce valoare va returna apelul
int F(int u, int v, int t) {int m = (u + v) / 2;if (u >= v) { return u; }else if (m * m > t) { return F(u, m - 1, t); }else if (m * m < t) { return F(m + 1, v, t); }else { return m; }}
Trebuie doar să urmărim lanțul de apeluri recursive. Se observă că funcția dată calculează radicalul lui în timp logaritmic, folosind o tehnică asemănătoare căutării binare. Precizia este însă foarte proastă, fiind vorba de numere întregi.
Apelul #1: F(0, 63, 64)m = (0 + 63) / 2 = 31m * m = 961 > 64
Apelul #2: F(0, 30, 64)m = (0 + 30) / 2 = 15m * m = 225 > 64
Apelul #3: F(0, 14, 64)m = (0 + 14) / 2 = 7m * m = 49 < 64
Apelul #4: F(8, 14, 64)m = (8 + 14) / 2 = 11m * m = 121 > 64
Apelul #5: F(8, 10, 64)m = (8 + 10) / 2 = 9m * m = 81 > 64
Apelul #6: F(8, 9, 64)m = (8 + 9) / 2 = 8m * m = 64 = 64Se returnează 8.
Subiectul III. Problema 3.
O casă are camere și este reprezentată ca o matrice cu linii (numerotate de la la și coloane (numerotate de la la Camera de la linia și coloana este identificată prin perechea de numere Toate camerele, cu excepția celor situate pe prima linie și a celor situate pe prima coloană au câte un comutator. Acționarea comutatorului din camera (unde conduce la următorul rezultat: În fiecare dintre camerele (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 linii și coloane. Pozițiile conțin valoarea reprezentând faptul că luminile sunt aprinse în camerele respective, iar celelalte poziții conțin valoarea reprezentând faptul că luminile sunt stinse. Comutatoarele din care camere trebuie acționate pentru a stinge luminile din toate camerele?
Acționăm comutatoarele din camerele și
b) Scrieți o funcție C++ care primește parametri de intrare: o matrice de și de reprezentând starea luminilor din camere, dimensiunile și ale acesteia, precum și două numere naturale reprezentând o cameră 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 Nu este necesară validarea parametrilor de intrare.
// NMAX = numărul maxim de linii ale matricei// MMAX = numărul maxim de coloane ale matriceivoid change(bool A[NMAX][MMAX], int n, int m, int i, int j) { A[i][j] ^= true; A[i - 1][j] ^= true; A[i][j - 1] ^= true; A[i - 1][j - 1] ^= true;}
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 de și de reprezentând starea luminilor din camere, precum și dimensiunile și ale acesteia. Funcția trebuie să returneze 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 Î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 și coloana Când ne aflăm la poziția acționăm comutatorul doar dacă poziția este setată la Asta pentru că acea poziție nu va mai fi vizitată niciodată, și nu are sens să o facem mai devreme, stricând eventual alte poziții deja vizitate.
Dacă poziția curentă se află pe ultima linie și poziția a rămas setată la î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 a rămas nu există soluție. Când ne aflăm la ultima poziție, adică testăm și dacă elementul de pe a rămas 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 ș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ă.
bool check(bool A[NMAX][MMAX], int n, int m) { bool a[NMAX][MMAX]; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) a[i][j] = A[i][j]; for (int i = 1; i < n; i++) for (int j = 1; j < m; j++) { if (a[i - 1][j - 1]) change(a, n, m, i, j); if (i == n - 1 && a[i][j - 1]) return false; if (j == m - 1 && a[i - 1][j]) return false; if (i == n - 1 && j == m - 1 && a[i][j]) return false; } return true;}
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