Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Rezolvare subiecte admitere - varianta 41 ====== ===== Exercitiul 1===== Numim graf complementar al unui graf neorientat $math[G] graful neorientat $math[G_1] cu aceeasi multime a nodurilor ca si $math[G] si cu proprietatea ca doua noduri sunt adiacente in $math[G_1] daca si numai daca nu sunt adiacente in $math[G]. Daca $math[G] are $math[n] noduri si $math[m] muchii, cate muchii are $math[G_1]. **a)** exact ''n(n-1)/2-m'' **b)** exact ''n-m'' **c)** exact ''(n-1)/2'' **d)** minimum ''n(n-1)/2-m'' **e)** minimum ''n-m'' **f)** maximum ''n(n-1)/2-m'' ==== Solutie ==== Explicatii: ce este un graf **neorientat**, **complementar**, **complet** la ce se refera **adiacenta**. Rezolvare: Intr-un graf complet se gasesc ''n(n-1)'' muchii **orientate**, cu conditia ca o muchie sa nu poata avea acelasi nod ca sursa/destinatie. Asadar avem ''n(n-1)/2'' muchii neorientate. Daca in $math[G] se gasesc $math[m] muchii, atunci in $math[G_1] se vor gasi $math[\frac{n(n-1)}{2}-m] muchii. ===== Exercitiul 2===== Cu ce expresie trebuie completata secventa lipsa (marcata prin ''...'') din functia urmatoare, pentru ca ''f(x,2)'' sa aiba ca rezultat suma exponentilor factorilor primi ce intra in descompunerea lui ''x''? <code C> int f(int x, int d){ if(...) return 1; if(x%d==0) return 1+f(x/d,d); return f(x, d+1); } </code> **a)** ''x==1'' **b)** ''x==0'' **c)** ''x<d/2'' **d)** ''x<d'' **e)** ''x>d'' **f)** ''x<%%=%%d'' ==== Solutie ==== Context: numerele prime compun toate numerele naturale (comparatie cu tabelul lui Mendeleev). Factorii primi sunt acele numere prime care compun un numar. Iar exponentii sunt puterile la care acesti factori se afla. Gauss, a demonstrat in aprox. 1800, ca fiecare numar natural are o descompunere unica in factori primi. Programul de mai sus, testeaza divizibilitatea lui ''x'' cu fiecare numar natural ''d'', incepand cu 2. Daca ''x'' se divide cu ''d'', procesul se reia cu ''x/d'' si ''d''. Cateva observatii: * daca un numar ''x'' (e.g. 12) se divide cu un ''d=k*n'' (e.g. ''6''), atunci el se divide cu ''2'', iar acest divizor va fi gasit inaintea lui ''6''. De aceea este corect sa incrementam cu ''1'' pe ''d'', fara a fi nevoie sa luam in considerare doar numerele prime * daca un numar ''x'' (e.g. 9) se divide de mai multe ori cu un ''d'', atunci apelul ''1+f(x/d,d)'' va contoriza in mod corect de cate ori se divide ''x'' cu ''d''. Acesta este **exponentul** mentionat in intrebare. Se observa ca dupa ce am //eliminat// din ''x'' pe divizorul sau ''d'', putem continua cu un divizor mai mare (nu este necesar sa reluam cautarea de la ''d=2''). Algoritmul descris mai sus, poate fi implementat in doua feluri: <code C> int f(int x, int d){ if(x==1) return 0; if(x%d==0) return 1+f(x/d,d); return f(x, d+1); } </code> In acest caz, cand ''x==1'', numarul a fost deja descompus si toti exponentii au fost contorizati. sau: <code C> int f(int x, int d){ if(x==d) return 1; if(x%d==0) return 1+f(x/d,d); return f(x, d+1); } </code> In acest caz, cand ''x==d'' inseamna ca am intalnit ultima **aparitie** a lui ''d'' ca divizor a lui ''x'' (garantat sa existe si sa fie gasit de procedura de mai sus). Singurul raspuns cu care programul de mai sus s-ar comporta la fel este ''x<=d'' ===== Exercitiul 3===== Instructiunea care afiseaza cea mai din stanga pozitie unde se afla valoarea intreaga ''x'', sau afiseaza ''-1'', daca ''x'' nu apare in tabloul unidimensional ''a'' cu ''n'' elemente intregi. <code C> // Varianta 1 for (i=0 ; i<n && a[i] == x ; i++) if (i < n) printf("%d", i); else printf("-1"); </code> <code C> // Varianta 2 for (i=0 ; i<n && a[i] != x ; i++) if (i == n) printf("%d", i); else printf("-1"); </code> <code C> // Varianta 3 for (i=0 ; i<n && a[i] == x ; i++) if (i == n) printf("%d", i); else printf("-1"); </code> <code C> // Varianta 4 for (i=0 ; i<n && a[i] != x ; i++); if (i < n) printf("%d", i); else printf("-1"); </code> <code C> // Varianta 5 for (i=0 ; i==n && a[i] != x ; i++); if (i == n) printf("%d", i); else printf("-1"); </code> <code C> // Varianta 6 for (i=0 ; i<n ; i++) if (a[i] == x) printf("%d", i); else printf("-1"); </code> ==== Solutie ==== Consideram ca ''a'' contine elementele: ''1,2,3,2,4,5,1'' si ca ''x=2''. Atunci programul cautat trebuie sa intoarca pozitia ''1'', adica **prima** aparitie a lui ''2'' in sir. Analizand cu atentie fiecare varianta: * //Varianta 1//: va iesi din ''for'' daca primul element nu este egal cu ''x'', prin urmare este incorecta. * //Varianta 2//: va iesi din ''for'' cand prima aparitie a lui ''x'' in ''a'' va fi gasita, insa daca aceasta va fi raportata **doar** daca ''i==n'', deci varianta este gresita. * //Varianta 3//: aceeasi problema ca //Varianta 1// * //Varianta 4//: instructiunea ''for'' se va executa fara instructiuni interioare pana cand: (i) ''a[i] == x'' sau (ii) ''i==n''. Daca ''i<n'', pozitia va fi raportata: **raspuns corect**. * //Varianta 5//: nu se va intra in ''for'' decat daca tabloul are dimensiune 0 (''i==n'') * //Varianta 6//: va afisa **toate** pozitiile pe care se afla ''x'', nu doar prima. ===== Exercitiul 4===== Apelul ''F(7)'' returneaza: <code C> int F(int N) { if (N==0) return 1; return F(N-1) + F(N-1); } </code> **a)** 60 **b)** 64 **c)** 120 **d)** 128 **e)** 240 **f)** 256 ==== Solutie ==== Recurenta implementata de program este: $math[F(N)=2*F(N-1)] pt $math[N > 0] si $math[F(0)=1]. Raspunsul asadar trebuie sa fie o putere a lui 2 (ceea ce exclude variantele a) c) d)). Se observa cu usurinta ca $math[F(N)=2^N] asadar raspunsul corect este $math[2^7=128]. ===== Exercitiul 5===== Se considera un sire de caractere ''s''. Stabiliti rezultatul afisarii pe ecran, in urma executarii urmatoarei secvente de program: <code C> char s[15]="ABCDEFG", *p, x[15]; p = s; p += 4; strcpy(x,s+2); strcat(x,p); printf("%s",x); </code> **a)** ''ABCEFG'' **b)** ''BCDEFGDEFG'' **c)** ''CDEFGEFG'' **d)** ''CDEFGABCD'' **e)** ''DEFGDEFG'' **f)** ''EFGEFG'' ==== Solutie ==== Varianta c. Slide-uri cu memory model-ul pentru char-uri in C. ===== Exercitiul 6===== Precizati care dintre urmatoarele expresii are valoarea ''1|TRUE'' daca si numai daca numarul natural ''n'' este divizibil cu ''3'' si are ultima cifra ''4'' sau ''6''. A. ''n/3==0 && (n%10==4 || n%10 == 6)'' B. ''n%3==0 && (n%10==4 || n%10 == 6)'' C. ''(n%3==0 && n%10==4) || (n%3 == 0 && n%10 == 6)'' D. ''(n%3==0 && n%10==4) || n%10 == 6'' **a)** nici una **b)** B **c)** A,B **d)** B,C **e)** B,D **f)** D,A ==== Solutie ==== Discutie despre ''mod'' si ''div'' si importanta lor pentru CS. Instructiunea: * ''n%3==0'' verifica daca ''n'' este divizibil cu ''3'' iar * ''n%10==x'' verifica daca ultima cifra a lui ''n'' este egala cu ''x''. Prioritatea operatorilor (precedenta!). Variantele corecte sunt: B,C. iar raspunsul este d). ===== Exercitiul 7===== Se genereaza in ordine lexicografica toate tripletele vocala-consoana-vocala formate cu literele mari ''A,B,C,D,E'': ''ABA, ABE, ACA, ACE, ADA, ADE, EBA, EBE, ECA, ECE, EDA, EDE''. Daca se genereaza, folosind aceeasi metoda si aceleasi litere, toate tripletele consoana-vocala-consoana, stabiliti care dintre urmatoarele variante este o secventa de triplete generate unul imediat dupa celalalt: **a)** ''ACE ADA ADE'' **b)** ''BEC BED CAB'' **c)** ''BEC CEC DEC'' **d)** ''CEA CEB CEC'' **e)** ''DAC DAB DEB'' **f)** ''DAD DAC DAB'' ==== Solutie ==== Pentru a raspunde corect la o problema, trebuie sa identificam regulile de formare ale tripletelor. In exemplul ilustrat, tripletele sunt: * vocala-consoana-vocala iar in cerinta tripletele construite vor fi: **consoana-vocala-consoana** asadar: * primul triplet este ''BAB'' * unui triplet $math[x_iy_jz_k] ii urmeaza tripletul $math[x_iy_jz_{k+1}], unde litera ''z_{k+1}'' este urmatoarea consoana, **daca aceasta exista** * unui triplet $math[x_iy_jz_n] ii urmeaza tripletul $math[x_iy_{j+1}B], unde litera ''y_{j+1}'' este urmatoarea vocala, **daca aceasta exista** iar $math[B] este prima vocala. * unui triplet de forma $math[x_iy_mz_n] ii urmeaza tripletul $math[x_{i+1}AB] unde litera $math[x_{i+1}] este urmatoarea consoana, **daca aceasta exista**, $math[B] este prima consoana, iar $math[A] este prima vocala. Putem incepe prin a elimina urmatoarele variante, din cauza ca tripletii nu respecta forma **consoana-vocala-consoana**: ** variantele **a)** si **d)** Apoi pe baza regulilor de mai sus, putem elimina: * varianta **c)**, din cauza ca, la primele doua triplete se schimba prima litera fara a se schimba restul. * varianta **c)**, intre ultimele doua triplete se schimba prima vocala, fara a se modifica ultimele litere * varianta **e)**, la tripletele ''DAC DAB'' nu se respecta ordinea lexicografica la ultima litera * varianta **f)**, la fel, pentru ''DAD DAC''. Varianta corecta este **b)**. ===== Exercitiul 8===== Se considera numerele naturale ''m'' si ''n'' ($math[0 \leq m \leq 10, 0 \leq n \leq 10]) si subprogramul ''Ack(m,n)'', care calculeaza valoarea functiei Ackermann pentru valorile ''m'' si ''n''. Precizati numarul de apeluri recursive ale subprogramului ''Ack'' pentru valorile ''m=1'' si ''n=2'', ''Ack(1,2)''. <code C> int Ack (int m, int n) { if (m == 0) return n+1; else if (m > 0 && n == 0) return Ack(m-1,1); else return Ack(m-1,Ack(m,n-1)); } </code> **a)** de 5 ori **b)** de 6 ori **c)** de 7 ori **d)** de 8 ori **e)** de 9 ori **f)** de 10 ori ==== Solutie ==== Functia lui Ackermann (calculata in program) este primul exemplu de functie: * **total calculabila** - pentru care putem scrie un algoritm care se termina intotdeauna si care intoarce raspunsul corect (este cel din exercitiu) * care **nu** este **primitiv-recursiva**. Acest concept are o definitie mai complicata si a fost studiat pentru a afla limitele a ce pot rezolva algoritmii. Informal o functie este **primitiv-recursiva** o putem calcula cu o combinatie de expresii ''for'' (la fiecare intrare intr-un ciclu stim exact cate iteratii se vor desfasura). Pentru a identifica numarul de apeluri recursive ''Ack(1,2)'' urmarim programul de mai sus, apel cu apel: <code C> Ack(1,2) Ack(0,Ack(1,1)) //contorizam apelul Ack(1,1) din parametru Ack(0,Ack(0,Ack(1,0))) Ack(0,Ack(0,Ack(0,1))) Ack(0,Ack(0,2)) Ack(0,3) 4 </code> Primul apel nu este recursiv, deci nu il vom numara. Pe a doua linie avem doua apeluri recursive. Apoi ''Ack(1,1)'' va genera inca doua apeluri recursive, iar ''Ack(1,0)'' va genera un singur apel recursiv, urmare caruia apelurile generate anterior vor intoarce. Raspunsul corect este **a)** de cinci ori. ===== Exercitiul 9===== Un arbore cu radacina are 359 de noduri numerotate de la 1 la 359. Daca vectorul de tati al acestui arbore (vector notat cu ''t'') are proprietatea ca ''t[i]=[i/2]'', pentru orice ''i'' de la 1 la 359, unde ''[x]'' reprezinta partea intreaga a numarului ''x'', atunci nuarul de noduri care au exact un descendent direct in acest arbore este: **a)** 178 **b)** 9 **c)** 4 **d)** 3 **e)** 1 **f)** 0 ==== Solutie ==== ===== Exercitiul 1===== **a)** **b)** **c)** exact **d)** **e)** **f)** ==== Solutie ====