Sari la conținut

Bac informatică 2020 Varianta 2, rezolvată

Varianta 2 de la Bacalaureatul la Informatică (Mate-Info, C/C++): conține 3 subiecte (90 de puncte) și are un timp de lucru de 3 ore. Sub fiecare exercițiu găsești răspunsul corect și rezolvarea.

Subiecte oficiale publicate de Ministerul Educației. Rezolvări realizate de subac. Actualizat la .

📌 Despre Varianta 2 din 2020

Varianta 2 din 2020 acoperă operatorul %, recursivitatea, backtracking-ul, grafurile și arborii, structurile imbricate, matricele, șirurile de caractere și fișierele. Este o variantă completă, bună pentru exersat atenția la detalii.

Subiectul I (20p): 5 grile: valoarea minimă a unei expresii cu %, un subprogram recursiv care afișează la coborâre și la întoarcere, o problemă de backtracking cu meniuri de câte patru feluri de mâncare, componentele tare conexe ale unui graf orientat desenat și numărul maxim de fii ai unui nod într-un arbore cu 20 de noduri și 10 frunze.

Subiectul al II-lea (40p): Un algoritm în pseudocod care taie ultimele cifre ale fiecărui număr citit cât timp ele sunt mai mari decât cifra dinaintea lor, o structură figura pentru un cerc, cu un câmp centru care este tot o structură, și o secvență care completează o matrice cu + sub diagonala principală și cu litere de la a încolo pe diagonală și deasupra ei.

Subiectul al III-lea (30p): Un subprogram (multiplu) care returnează cel mai mic multiplu nenul al lui n care este pătrat perfect, o problemă cu șiruri de caractere în care literele din „citate”, adică dintre < și >, devin litere mari, și o problemă de eficiență cu „vârfurile” unui șir citit din fișierul bac.in.

⚠️ La ce să fii atent

  • La I.1: Nu te opri la x=1000! Pentru x=1999, x-999 este 1000, iar restul împărțirii la 1000 este 0, deci valoarea minimă este 0.
  • La I.2: Fii atent unde stă cout<<x! Nu face parte din else, deci se execută la fiecare apel, după apelul recursiv. Zerourile apar la coborâre, iar numerele la întoarcere: 0071249.
  • La II.1.a: Un număr cu o singură cifră se reduce mereu la 0, pentru că cifra dinaintea lui este 0. De aceea 7 nu adaugă nimic la sumă, iar rezultatul este 2020.
  • La II.1.b: Ca să se afișeze niciunul, fiecare număr trebuie să se reducă la 0, adică să aibă cifrele în ordine strict crescătoare, de exemplu 1234 și 5678.
  • La III.3: Citește cu atenție regula de egalitate! În exemplu, vârfurile 10 și 11 au amândouă diferența dintre vecini 2, iar răspunsul este 11, cel mai mare dintre ele. Ține minte doar ultimele trei numere citite, fără să salvezi șirul într-un tablou.

Subiectul I

20 de puncte
  1. Exercițiul 1 4p Expresii și operatori

    Variabila x este de tip întreg și poate memora un număr din intervalul [1000,2000].

    Indicați valoarea minimă pe care o poate avea expresia C/C++ alăturată.

    (x-999)%1000
    • a)0
    • b)2
    • c)999
    • d)1000
    Vezi rezolvarea

    Răspuns corect: a) 0

    Cum x ia valori de la 1000 la 2000, x-999 ia valori de la 1 la 1001.

    Restul împărțirii la 1000 al acestor numere este:

    • 1…999 → chiar ele, deci de la 1 la 999
    • 1000 → 0
    • 1001 → 1

    Valoarea minimă este deci 0, obținută pentru x=1999. Varianta d) nici n-ar fi posibilă: un rest la împărțirea cu 1000 nu poate fi 1000.

  2. Exercițiul 2 4p Recursivitate

    Subprogramul f este definit alăturat.

    Indicați ce se afișează în urma apelului de mai jos.

    f(9);

    void f(int x)
    { if(x>=2)
      { if(x%2==0) cout<<0;  |  printf("0");
        f(x/2);
      }
      else cout<<7;  |  printf("7");
      cout<<x; | printf("%d",x);
    }
    • a)91
    • b)942
    • c)0071
    • d)0071249
    Vezi rezolvarea

    Răspuns corect: d) 0071249

    Subprogramul afișează x după apelul recursiv, deci numerele ies în ordine inversă față de coborâre, iar zerourile ies la coborâre:

    apelce afișează la intrarece afișează la ieșire
    f(9)— (9 e impar)9
    f(4)04
    f(2)02
    f(1)7 (1 < 2) și apoi 1—

    Citite în ordinea execuției: 0, 0, 7, 1, 2, 4, 9 — adică 0071249, varianta d).

    Capcana e că cout<<x nu e în else: se execută la fiecare apel, și la cele care au recursivitate, și la cel care oprește coborârea.

  3. Exercițiul 3 4p Backtracking

    Utilizând metoda backtracking, se generează toate meniurile care cuprind câte patru feluri distincte de mâncare din mulțimea {aperitiv, desert, legume, paste, salată, supă}, fiecare meniu respectând următoarele condiții:

    • dacă există aperitiv, este servit primul;

    • dacă există desert, este servit ultimul;

    • NU sunt servite legume și salată în același meniu;

    • dacă există paste și supă în același meniu, pastele NU sunt servite înainte de supă.

    Două meniuri sunt distincte dacă diferă prin cel puțin un fel de mâncare sau prin ordinea servirii acestora. Primele cinci meniuri generate sunt, în această ordine:

    1. (aperitiv, legume, paste, desert)
    2. (aperitiv, legume, supă, desert)
    3. (aperitiv, legume, supă, paste)
    4. (aperitiv, paste, legume, desert)
    5. (aperitiv, paste, salată, desert)

    Indicați al șaselea meniu generat.

    • a)(aperitiv, salată, paste, desert)
    • b)(aperitiv, salată, supă, desert)
    • c)(aperitiv, salată, supă, paste)
    • d)(aperitiv, supă, legume, desert)
    Vezi rezolvarea

    Răspuns corect: a) (aperitiv, salată, paste, desert)

    Felurile se iau în ordinea din mulțime: 1 aperitiv, 2 desert, 3 legume, 4 paste, 5 salată, 6 supă. Se generează aranjamente de câte patru (ordinea contează), în ordine crescătoare a indicilor, iar cele care încalcă o condiție se resping:

    meniu
    1aperitiv, legume, paste, desert
    2aperitiv, legume, supă, desert
    3aperitiv, legume, supă, pastesupă înaintea pastelor, deci e bun
    —aperitiv, legume, supă, salatărespins: legume cu salată
    4aperitiv, paste, legume, desert
    —aperitiv, paste, legume, supărespins: pastele înaintea supei
    5aperitiv, paste, salată, desert
    —aperitiv, paste, salată, supărespins: pastele înaintea supei
    —aperitiv, salată, legume, …respins: salată cu legume
    6aperitiv, salată, paste, desert

    Primele cinci coincid cu cele date în enunț, ceea ce confirmă ordinea. Al șaselea este a).

  4. Exercițiul 4 4p Grafuri orientate

    Un graf orientat cu 5 vârfuri, numerotate de la 1 la 5, este reprezentat alăturat.

    Indicați numărul de componente tare conexe ale grafului.

    Figura din enunț
    • a)1
    • b)2
    • c)3
    • d)4
    Vezi rezolvarea

    Răspuns corect: b) 2

    Din desen, arcele sunt: 1→2, 2→5, 5→1, 5→3, 5→4, 3→4, 4→3.

    Două vârfuri sunt în aceeași componentă tare conexă dacă se poate ajunge de la unul la altul în amândouă sensurile.

    • 1, 2 și 5 formează un circuit: 1→2→5→1. Deci sunt împreună.
    • 3 și 4 sunt legate în amândouă sensurile: 3→4 și 4→3. Deci sunt împreună.

    Din 5 se ajunge la 3 și la 4, dar înapoi nu: nu există niciun arc care să iasă din {3,4} spre restul grafului. Deci cele două grupuri nu se pot uni.

    Sunt 2 componente tare conexe: {1,2,5} și {3,4} — varianta b).

  5. Exercițiul 5 4p Arbori

    Un arbore cu rădăcină are 20 de noduri, dintre care 10 noduri de tip “frunză”.

    Indicați numărul maxim de noduri care au același “tată” în acest arbore.

    • a)5
    • b)7
    • c)10
    • d)15
    Vezi rezolvarea

    Răspuns corect: c) 10

    Arborele are 20 de noduri, dintre care 10 sunt frunze, deci 10 noduri au cel puțin un fiu.

    Ca să aibă un nod cât mai mulți fii, restul nodurilor interne trebuie „consumate” cât mai economic: le înșiri într-un lanț — rădăcina cu un fiu, acela cu un fiu, și tot așa, nouă noduri interne. Al zecelea nod intern, cel din capătul lanțului, primește drept fii toate cele 10 frunze.

    Mai mult de 10 nu se poate: un nod cu 11 fii ar avea printre ei cel puțin un nod intern, iar acela ar avea nevoie de un fiu propriu — care ar trebui să fie tot o frunză, și n-au mai rămas. Răspunsul este c).

Subiectul al II-lea

40 de puncte
  1. Exercițiul 1.a 6p Algoritmi în pseudocod

    Algoritmul alăturat este reprezentat în pseudocod.

    S-a notat cu a%b restul împărțirii numărului natural a la numărul natural nenul b și cu [c] partea întreagă a numărului real c.

    Scrieți ce se afișează dacă se citesc, în această ordine, numerele 5, 127, 2019, 1005, 7, 1900.

    citește n (număr natural nenul)
     m←0
    ┌pentru i←1,n execută
    │ citește x (număr natural)
    │┌cât timp x%10 > [x/10]%10 execută
    ││ x←[x/10]
    │└■
    │ m←m+x
    └■
    ┌dacă m>0 atunci scrie m
    │altfel scrie "niciunul"
    └■
    Vezi rezolvarea

    Răspunsul din barem: 2020

    Bucla cât timp taie ultima cifră a lui x atât timp cât aceasta e mai mare decât cifra dinaintea ei. Ce rămâne se adună la m.

    x cititcum se reducerămâne
    1277>2 → 12; 2>1 → 1; 1>0 → 00
    20199>1 → 201; 1>0 → 20; 0>2 nu20
    10055>0 → 100; 0>0 nu100
    77>0 → 00
    19000>0 nu, se oprește din prima1900

    Suma este 0+20+100+0+1900 = 2020, iar cum m>0, se afișează 2020.

    De reținut: un număr cu o singură cifră se reduce mereu la 0, fiindcă „cifra dinaintea ei” e 0.

  2. Exercițiul 1.b 6p Algoritmi în pseudocod

    Același algoritm ca la 1.a.

    S-a notat cu a%b restul împărțirii numărului natural a la numărul natural nenul b și cu [c] partea întreagă a numărului real c.

    Dacă primul număr citit este 2, scrieți un set de numere distincte din intervalul [10^3,10^4) care pot fi citite în continuare astfel încât, în urma executării algoritmului, să se afișeze mesajul niciunul.

    Vezi rezolvarea

    1234 5678

    Ca să se afișeze niciunul, suma m trebuie să fie 0, iar cum toate numerele citite sunt naturale, fiecare dintre ele trebuie să se reducă la 0.

    Un număr se reduce la 0 doar dacă bucla îi taie toate cifrele, adică dacă la fiecare pas ultima cifră e mai mare decât cea dinaintea ei. Asta înseamnă un număr cu cifrele strict crescătoare.

    1234: 4>3 → 123, 3>2 → 12, 2>1 → 1, 1>0 → 0. La fel 5678. Amândouă sunt în intervalul cerut și sunt distincte, deci se afișează niciunul.

    Orice altă pereche cu cifre strict crescătoare merge la fel de bine: 1259 și 3456, de exemplu.

  3. Exercițiul 1.c 10p Algoritmi în pseudocod

    Același algoritm ca la 1.a.

    S-a notat cu a%b restul împărțirii numărului natural a la numărul natural nenul b și cu [c] partea întreagă a numărului real c.

    Scrieți programul C/C++ corespunzător algoritmului dat.

    Vezi rezolvarea
    #include <iostream>
    using namespace std;
    
    int main()
    {
        int n, m = 0, x;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> x;
            while (x % 10 > x / 10 % 10)
                x = x / 10;
            m = m + x;
        }
        if (m > 0)
            cout << m;
        else
            cout << "niciunul";
        return 0;
    }
  4. Exercițiul 1.d 6p Algoritmi în pseudocod

    Același algoritm ca la 1.a.

    S-a notat cu a%b restul împărțirii numărului natural a la numărul natural nenul b și cu [c] partea întreagă a numărului real c.

    Scrieți în pseudocod un algoritm echivalent cu cel dat, înlocuind adecvat structura pentru...execută cu o structură repetitivă de alt tip.

    Vezi rezolvarea

    `` citește n (număr natural nenul) m←0 i←1 ┌cât timp i≤n execută │ citește x (număr natural) │┌cât timp x%10 > [x/10]%10 execută ││ x←[x/10] │└■ │ m←m+x │ i←i+1 └■ ┌dacă m>0 atunci scrie m │altfel scrie "niciunul" └■ ``

    pentru i←1,n face singur trei lucruri: pornește contorul de la 1, verifică dacă a depășit n și îl crește la fiecare pas. Cu cât timp trebuie scrise toate trei separat: inițializarea înainte de buclă, condiția i≤n în capul ei și creșterea i←i+1 ca ultimă instrucțiune din corp.

    Dacă ai uita i←i+1, bucla n-ar mai avea cum să se oprească.

  5. Exercițiul 2 6p Tipul înregistrare (struct)

    Variabila c memorează date specifice unui cerc: coordonatele reale (abscisa și ordonata), în planul xOy, ale centrului cercului, precum și lungimea razei acestuia.

    Știind că expresiile C/C++ de mai jos au ca valori numere reale reprezentând datele specifice ale unui cerc, scrieți definiția unei structuri cu eticheta figura, care permite memorarea datelor precizate, și declarați corespunzător variabila c.

    c.centru.x     c.centru.y     c.raza
    Vezi rezolvarea

    ```cpp struct punct { double x; double y; };

    struct figura
    {
        punct centru;
        double raza;
    };

    figura c; ```

    Expresiile din enunț cer două niveluri: c.centru.x înseamnă că c are un câmp centru, iar acela are la rândul lui câmpurile x și y. Deci e nevoie de o structură în altă structură.

    Coordonatele și raza sunt numere reale, deci câmpurile sunt double (merge și float). Numele lor nu sunt la alegere: centru, x, y și raza sunt impuse de expresiile date, iar eticheta structurii mari trebuie să fie figura, cum cere enunțul.

  6. Exercițiul 3 6p Tablouri bidimensionale

    Variabilele i și j sunt de tip întreg, iar variabila m memorează un tablou bidimensional cu 7 linii și 7 coloane, numerotate de la 0 la 6, cu elemente de tip char. Inițial, fiecare element memorează caracterul *.

    Fără a utiliza alte variabile, scrieți secvența de instrucțiuni de mai jos, înlocuind punctele de suspensie astfel încât, în urma executării secvenței obținute, variabila m să memoreze tabloul alăturat.

    for(i=0;i<7;i++)
      for(j=0;j<7;j++)
       ..................
    abcdefg
    +abcdef
    ++abcde
    +++abcd
    ++++abc
    +++++ab
    ++++++a
    Vezi rezolvarea

    ``cpp for (i = 0; i < 7; i++) for (j = 0; j < 7; j++) if (j < i) m[i][j] = '+'; else m[i][j] = 'a' + j - i; ``

    Privește tabloul cerut pe diagonală: pe diagonala principală, unde i=j, stă peste tot litera a. Deasupra ei literele merg mai departe — b, c, d — iar sub ea sunt doar +.

    Asta dă regula:

    • dacă j<i (sub diagonală) → '+'
    • altfel → litera aflată la j-i poziții după a, adică 'a'+j-i

    În C/C++ un caracter e un număr, deci 'a'+j-i e chiar litera dorită: pentru i=0, j=6 iese 'a'+6, adică 'g'.

Subiectul al III-lea

30 de puncte
  1. Exercițiul 1 10p Subprograme

    Subprogramul multiplu are un singur parametru, n, prin care primește un număr natural (n∈[1,10^4]). Subprogramul returnează cel mai mic multiplu nenul al lui n cu proprietatea că este pătrat perfect.

    Scrieți definiția completă a subprogramului.

    Exemplu: dacă n=72 sau n=144, subprogramul returnează numărul 144 (144=12^2).

    Vezi rezolvarea
    int multiplu(int n)
    {
        for (int k = 1; ; k++)
        {
            int v = n * k;
            int r = (int) sqrt((double) v);
            while (r * r > v)
                r--;
            while ((r + 1) * (r + 1) <= v)
                r++;
            if (r * r == v)
                return v;
        }
    }
  2. Exercițiul 2 10p Șiruri de caractere

    Numim citat într-un text o secvență de caractere din acel text care începe cu un caracter < și se termină cu un caracter >, celelalte caractere ale secvenței fiind diferite de < și >.

    Un text de cel mult 100 de caractere (litere mici ale alfabetului englez, spații și caracterele < și >) conține cel puțin un citat. Textul nu conține alte caractere < și > decât cele care mărginesc citatele, și oricare două citate nu au nici caractere < și > și nici alte caractere în comun.

    Scrieți un program C/C++ care citește de la tastatură un text de tipul precizat și îl transformă în memorie prin înlocuirea tuturor literelor mici cuprinse în citate cu literele mari corespunzătoare, celelalte rămânând nemodificate, ca în exemplu. Programul afișează pe ecran textul obținut.

    Exemplu: pentru textul mai bine sa fii un <om de valoare> decat un <om de succes> se afișează mai bine sa fii un <OM DE VALOARE> decat un <OM DE SUCCES>

    Vezi rezolvarea
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main()
    {
        char s[101];
        cin.getline(s, 101);
        int inauntru = 0;
        for (int i = 0; s[i] != '\0'; i++)
        {
            if (s[i] == '<')
                inauntru = 1;
            else if (s[i] == '>')
                inauntru = 0;
            else if (inauntru == 1 && s[i] >= 'a' && s[i] <= 'z')
                s[i] = s[i] - 'a' + 'A';
        }
        cout << s;
        return 0;
    }
  3. Exercițiul 3.a 2p Algoritmi eficienți

    Se numește vârf într-un șir de numere naturale un termen al șirului care este strict mai mare decât fiecare dintre cei doi termeni vecini cu el, aflați în șir pe poziția din stânga, respectiv din dreapta sa.

    Fișierul bac.in conține un șir de cel mult 10^6 numere naturale din intervalul [0,10^9], separate prin câte un spațiu. Se cere să se afișeze pe ecran vârful din șirul aflat în fișier pentru care valoarea absolută a diferenței dintre cei doi vecini ai săi este minimă. Dacă există mai multe astfel de numere, se afișează cel mai mare dintre ele, iar dacă nu există niciun vârf, se afișează pe ecran mesajul nu exista. Proiectați un algoritm eficient din punctul de vedere al timpului de executare și al spațiului de memorie utilizat.

    Exemplu: dacă fișierul conține șirul 2 7 10 5 6 2 1 3 20 17 9 11 7 3 10 6 2 se afișează pe ecran 11

    Descrieți în limbaj natural algoritmul proiectat, justificând eficiența acestuia.

    Vezi rezolvarea

    Timp de executare: O(n) · Memorie: O(1)

    Se citește șirul o singură dată, din fișier, fără să fie păstrat.

    La orice moment țin minte doar trei valori consecutive: a, b și c. Dacă b>a și b>c, atunci b este vârf, iar diferența dintre vecinii lui este |a-c|.

    Păstrez două valori: cea mai mică diferență găsită până acum și vârful care a dat-o. Un vârf nou îl înlocuiește pe cel păstrat dacă are diferența mai mică sau dacă are aceeași diferență, dar e mai mare — exact regula din enunț. Dacă la final n-am găsit niciun vârf, afișez nu exista.

    După fiecare pas, „fereastra” se mută cu o poziție: a ia valoarea lui b, b pe a lui c, iar c devine numărul următor din fișier.

    De ce e eficient: fiecare număr e citit și comparat o singură dată, deci timpul e O(n). Memoria e O(1): oricât de lung ar fi șirul — și poate avea 10⁶ numere — se rețin doar cele trei valori din fereastră și cele două valori ale răspunsului, niciodată șirul întreg.

  4. Exercițiul 3.b 8p Algoritmi eficienți

    Se numește vârf într-un șir de numere naturale un termen al șirului care este strict mai mare decât fiecare dintre cei doi termeni vecini cu el, aflați în șir pe poziția din stânga, respectiv din dreapta sa.

    Fișierul bac.in conține un șir de cel mult 10^6 numere naturale din intervalul [0,10^9], separate prin câte un spațiu. Se cere să se afișeze pe ecran vârful din șirul aflat în fișier pentru care valoarea absolută a diferenței dintre cei doi vecini ai săi este minimă. Dacă există mai multe astfel de numere, se afișează cel mai mare dintre ele, iar dacă nu există niciun vârf, se afișează pe ecran mesajul nu exista. Proiectați un algoritm eficient din punctul de vedere al timpului de executare și al spațiului de memorie utilizat.

    Exemplu: dacă fișierul conține șirul 2 7 10 5 6 2 1 3 20 17 9 11 7 3 10 6 2 se afișează pe ecran 11

    Scrieți programul C/C++ corespunzător algoritmului proiectat.

    Vezi rezolvarea
    #include <fstream>
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    int main()
    {
        ifstream f("bac.in");
        long long a, b, c, difMin = -1, varf = -1;
        if (!(f >> a) || !(f >> b))
        {
            cout << "nu exista";
            return 0;
        }
        while (f >> c)
        {
            if (b > a && b > c)
            {
                long long dif = llabs(a - c);
                if (difMin < 0 || dif < difMin || (dif == difMin && b > varf))
                {
                    difMin = dif;
                    varf = b;
                }
            }
            a = b;
            b = c;
        }
        f.close();
        if (difMin < 0)
            cout << "nu exista";
        else
            cout << varf;
        return 0;
    }

Vrei să vezi ce notă ai lua la examen?

Intră în modul simulare: ai cronometru de 3 ore, codul ți se compilează pe loc, iar la final primești nota conform baremului.

Începe simularea3 ore

Ai găsit o problemă sau ai o idee?

Despre ce este vorba?