BAC Informatică C/C++ - iunie 2025 (Varianta 1)
Toate subiectele sunt obligatorii. Se acordă zece puncte din oficiu. Timpul de lucru efectiv este de trei ore. Identificatorii utilizați în rezolvări trebuie să respecte precizările din enunț. În grafurile din cerințe oricare arc/muchie are extremități distincte și oricare două arce/muchii diferă prin cel puțin una dintre extremități.
Subiectul I
Indicați expresia C/C++ cu aceeași valoare ca a expresiei alăturate.
2025%2019+6a. 2025/2020+5
b. 2025/2021+8
c. 2025%2020+5
d. 2025%2021+8
Rezolvare
Calculăm expresia dată: , deci 2025%2019+6 are valoarea .
Evaluăm apoi variantele, ținând cont că / între întregi este împărțire întreagă, iar % este restul:
| variantă | calcul | valoare |
|---|---|---|
| a | , | 6 |
| b | , | 9 |
| c | , | 10 |
| d | , | 12 |
Singura expresie cu valoarea 12 este cea de la litera d.
Barem
Item grilă: expresia C/C++ cu aceeași valoare ca 2025%2019+6 (a. 2025/2020+5, b. 2025/2021+8, c. 2025%2020+5, d. 2025%2021+8). Barem: 4 puncte pentru litera d. Expresia dată valorează 6+6 = 12, iar 2025%2021+8 = 4+8 = 12. Fără punctaj parțial.
Subprogramul f este definit alăturat. Indicați ce se afișează în urma apelului f(3);.
void f(int n)
{ int i;
for(i=1;i<=n;i++)
if(i%2==0)
{ cout<<i;
f(i-1);
}
else
{ f(i-1);
cout<<i;
}
}a. 1211213
b. 123121
c. 123
d. 01201012013
Rezolvare
Cazul de bază este : bucla for nu se execută niciun pas, deci f(0) nu afișează nimic. Construim de jos în sus:
f(1): impar →f(0)(nimic), apoi1⟹ afișează 1f(2): →1; par →2, apoif(1)=1⟹ afișează 121f(3): →1; →2urmat def(1)=1, deci21; impar →f(2)=121urmat de3, deci1213
Concatenând: 1 + 21 + 1213 = 1211213, litera a.
Metoda de lucru: la recursie cu buclă, nu urmări apelurile în minte - calculează întâi ce afișează f(0), apoi f(1), apoi f(2), și folosește rezultatele deja obținute.
Barem
Item grilă: ce afișează apelul f(3) pentru subprogramul recursiv dat (pentru i de la 1 la n: dacă i este par se afișează i și apoi se apelează f(i-1), altfel se apelează f(i-1) și apoi se afișează i). Barem: 4 puncte pentru litera a (se afișează 1211213). Fără punctaj parțial.
Indicați o declarare a unui tablou bidimensional m, care poate memora maximum 100 de numere reale.
a. float m[2,50];
b. float m[4][25];
c. float m[10] x float m[10];
d. int m[100];
Rezolvare
Cerința are trei condiții: tabloul trebuie să fie bidimensional, să memoreze numere reale și să aibă cel mult 100 de elemente.
- a.
float m[2,50];- în C/C++ virgula dintre paranteze este operatorul virgulă, nu un separator de dimensiuni; declarația este echivalentă cufloat m[50];, deci un tablou unidimensional. - b.
float m[4][25];- bidimensional, elemente reale, elemente. Corect. - c.
float m[10] x float m[10];- nu este o sintaxă C/C++ validă. - d.
int m[100];- unidimensional și cu elemente întregi.
Răspunsul corect este litera b.
Barem
Item grilă: declararea unui tablou bidimensional care poate memora maximum 100 de numere reale. Barem: 4 puncte pentru litera b (float m[4][25], adică 4·25 = 100 elemente reale). Fără punctaj parțial.
Utilizând metoda backtracking, s-au generat toate codurile posibile pentru deblocarea unor telefoane, coduri de câte 6 cifre distincte, din mulțimea cifrelor, ordonată crescător. Fiecare cod are primele trei cifre impare și ultimele trei cifre pare. Primele patru coduri sunt 135024, 135026, 135028, 135042. Indicați penultimul cod generat.
a. 957862
b. 957846
c. 975862
d. 975846
Rezolvare
Codurile se generează în ordine lexicografică (crescătoare), pentru că la fiecare poziție cifrele se încearcă în ordinea crescătoare a mulțimii. Ultimul cod generat este deci cel mai mare cod posibil:
- primele trei cifre, impare distincte, cât mai mari:
9,7,5; - ultimele trei cifre, pare distincte, cât mai mari:
8,6,4.
Ultimul cod este 975864. Penultimul se obține modificând ultima poziție, singura care se schimbă cel mai des: cu prefixul 97586 fixat, cifrele pare rămase sunt 0, 2 și 4, deci codurile cu acest prefix sunt, în ordine, 975860, 975862, 975864. Penultimul cod generat este așadar 975862, litera c.
Atenție la capcană: penultimul NU se obține micșorând prima cifră - variantele a și b schimbă a doua cifră, ceea ce ar da coduri mult mai mici, generate cu mult înainte.
Barem
Item grilă de backtracking: coduri de 6 cifre distincte, primele trei impare și ultimele trei pare, generate în ordine lexicografică; se cere penultimul cod. Barem: 4 puncte pentru litera c (975862). Ultimul cod generat este 975864, iar penultimul se obține scăzând pe ultima poziție. Fără punctaj parțial.
Un graf orientat fără circuite are 10 vârfuri. Indicați numărul maxim de arce ale grafului.
a. 10
b. 45
c. 50
d. 90
Rezolvare
Într-un graf orientat fără circuite vârfurile pot fi numerotate astfel încât orice arc să meargă de la un vârf cu număr mai mic la unul cu număr mai mare (o sortare topologică). Atunci pentru fiecare pereche de vârfuri există cel mult un arc, iar numărul maxim de arce este numărul de perechi:
Răspunsul corect este litera b. Varianta d, , este numărul maxim de arce ale unui graf orientat oarecare cu 10 vârfuri - dar acolo ambele arce dintre două vârfuri ar forma un circuit de lungime 2, interzis aici.
Barem
Item grilă: numărul maxim de arce ale unui graf orientat fără circuite cu 10 vârfuri (a. 10, b. 45, c. 50, d. 90). Barem: 4 puncte pentru litera b (45). Fără punctaj parțial.
Subiectul al II-lea
Algoritmul următor este reprezentat în pseudocod.
citește m,n (numere naturale nenule, m ≤ n)
nr ← 0; i ← m
repetă
x ← 1
cât timp x*x < i execută
x ← x+1
dacă x*x = i atunci
nr ← i
altfel
i ← i+1
până când i > n sau nr ≠ 0
scrie nrScrieți ce se afișează în urma executării algoritmului dacă se citesc, în această ordine, numerele 7 și 17.
Rezolvare
Algoritmul caută primul pătrat perfect din intervalul : pentru fiecare determină cel mai mic cu și verifică dacă .
| i | cel mai mic x cu x·x ≥ i | x·x | x·x = i? |
|---|---|---|---|
| 7 | 3 | 9 | nu → i ← 8 |
| 8 | 3 | 9 | nu → i ← 9 |
| 9 | 3 | 9 | da → nr ← 9 |
Ciclul se oprește pentru că , deci se afișează 9.
Barem
Pentru algoritmul pseudocod dat (caută, pornind de la i = m, prima valoare i ≤ n care este pătrat perfect, verificând-o prin căutarea celui mai mic x cu x*x ≥ i; scrie acea valoare sau 0 dacă nu există), se cere ce se afișează pentru m = 7 și n = 17. Barem: 6 puncte pentru răspunsul corect: 9. Fără punctaj parțial.
Dacă pentru variabila n se citește valoarea 25, scrieți două numere distincte care pot fi citite pentru variabila m, astfel încât, în urma executării algoritmului, pentru fiecare dintre acestea, să se afișeze valoarea 25.
Rezolvare
Algoritmul afișează primul pătrat perfect din . Ca acesta să fie chiar 25, intervalul nu trebuie să conțină niciun pătrat perfect mai mic decât 25. Pătratele perfecte din apropiere sunt și , deci trebuie ca , adică (condiția dă limita superioară).
Două valori posibile: m = 17 și m = 20.
Verificare pentru : ia pe rând valorile 20, 21, 22, 23, 24 - niciuna pătrat perfect - apoi , unde și , deci se afișează 25.
Barem
Același algoritm. Se cere: două valori distincte ale lui m pentru care, cu n = 25, se afișează 25. Barem: 6 puncte pentru răspuns corect, câte 3 puncte pentru fiecare dintre cele două numere conform cerinței - oricare două numere naturale distincte din intervalul [17,25].
Scrieți programul C/C++ corespunzător algoritmului dat.
Rezolvare
Traducerea este directă, cu o singură atenție: repetă ... până când C din pseudocod devine do { ... } while (!C); în C/C++, pentru că while continuă cât timp condiția este adevărată, iar până când se oprește când devine adevărată.
#include <iostream>
using namespace std;
int main()
{
int m, n, nr, i, x;
cin >> m >> n;
nr = 0;
i = m;
do
{
x = 1;
while (x*x < i)
x = x+1;
if (x*x == i)
nr = i;
else
i = i+1;
} while (!(i > n || nr != 0));
cout << nr;
return 0;
}Condiția finală se poate scrie și direct în forma echivalentă while (i <= n && nr == 0);, obținută prin legile lui De Morgan.
Barem
Același algoritm. Se cere programul C/C++ corespunzător. Barem, total 10 puncte: declarare a variabilelor 1p; citire a datelor 1p; afișare a datelor 1p; instrucțiune de decizie 2p; instrucțiuni repetitive 3p (doar 2p dacă doar una dintre cele două structuri repetitive este conform cerinței); atribuiri 1p; corectitudine globală 1p. Structura repetă...până când se traduce prin do...while cu condiția negată.
Scrieți în pseudocod un algoritm echivalent cu cel dat, înlocuind structura cât timp...execută cu o structură repetitivă cu test final.
Rezolvare
Diferența esențială dintre cele două structuri: cât timp testează înainte și se poate executa de zero ori, pe când o structură cu test final se execută cel puțin o dată. Cum pornește de la 1, pentru avem deja și bucla originală nu face niciun pas - de aceea noua buclă trebuie protejată de o decizie:
citește m,n (numere naturale nenule, m ≤ n)
nr ← 0; i ← m
repetă
x ← 1
dacă x*x < i atunci
repetă
x ← x+1
până când x*x ≥ i
dacă x*x = i atunci
nr ← i
altfel
i ← i+1
până când i > n sau nr ≠ 0
scrie nrCondiția de oprire a noii bucle este negarea condiției de continuare a celei vechi: cât timp x*x < i devine până când x*x ≥ i.
Barem
Același algoritm. Se cere înlocuirea structurii cât timp...execută cu o structură repetitivă cu test final. Barem, total 6 puncte: utilizare a unei structuri repetitive cu test final 2p; aspecte specifice ale secvenței obținute 3p (câte un aspect: echivalență a algoritmului pentru cazul inițial x*x ≥ i, expresie logică pentru testul final); algoritm complet și corectitudine globală 1p. Punctul-cheie: o structură cu test final se execută cel puțin o dată, deci cazul în care bucla cât timp nu s-ar executa niciun pas trebuie protejat cu o decizie.
Un graf neorientat cu 6 noduri, numerotate de la 1 la 6, este reprezentat prin listele de adiacență alăturate. Scrieți mulțimea nodurilor și mulțimea muchiilor unui subgraf al acestuia, fără noduri izolate, care să fie graf eulerian.
1: 2, 3, 4, 6
2: 1, 3, 5
3: 1, 2, 5
4: 1, 5, 6
5: 2, 3, 4, 6
6: 1, 4, 5Rezolvare
Un graf este eulerian dacă este conex și toate vârfurile au grad par. Cel mai simplu subgraf de acest tip este un triunghi, în care fiecare vârf are gradul 2.
Din listele de adiacență, nodurile 1, 2 și 3 sunt legate două câte două: muchiile , și există toate. Alegem deci
Verificare: subgraful este conex, fiecare dintre cele trei noduri are gradul 2 (par) și niciun nod nu este izolat, deci este eulerian - ciclul eulerian este chiar .
Soluția nu este unică: de exemplu cu (un ciclu de lungime 4) este tot un răspuns corect.
Barem
Se cere un subgraf eulerian fără noduri izolate al grafului neorientat cu 6 noduri dat prin liste de adiacență. Barem: 6 puncte pentru răspuns corect, câte 2 puncte pentru fiecare aspect: mulțimea nodurilor unui subgraf fără noduri izolate, mulțimea muchiilor corespunzătoare mulțimii nodurilor alese, caracterul eulerian al grafului obținut. Orice subgraf conex cu toate gradele pare este acceptat.
Variabila p memorează simultan, pentru un tip de prăjitură, codul (un număr natural de două cifre), prețul (număr real) și un set de trei numere naturale din intervalul , reprezentând informații specifice, în această ordine: tipul glazurii, tipul cremei principale și numărul de blaturi. Știind că expresiile C/C++ de mai jos au ca valori codul, prețul, respectiv tipul glazurii pentru o prăjitură, scrieți definiția unei structuri cu eticheta prajitura, care permite memorarea datelor despre o prăjitură, și declarați corespunzător variabila p.
p.cod p.pret p.informatii[0]Rezolvare
Expresiile din enunț impun structura câmpurilor: p.cod și p.pret sunt câmpuri simple, iar p.informatii[0] arată că informatii este un tablou cu 3 elemente.
struct prajitura
{
int cod;
float pret;
int informatii[3];
};
prajitura p;Codul este un număr natural de două cifre și cele trei informații sunt numere naturale din , deci tipul int este suficient pentru toate; prețul este număr real, deci float (sau double). În C, declararea variabilei se scrie struct prajitura p;.
Barem
Se cere definiția unei structuri cu eticheta prajitura (cod - număr natural de două cifre, pret - număr real, informatii - trei numere naturale) și declararea variabilei p, astfel încât expresiile p.cod, p.pret și p.informatii[0] să fie valide. Barem: 6 puncte pentru răspuns corect - 4p pentru definirea structurii (câte 1p pentru: definire de bază a unei structuri, etichetă, câmpuri de tip simplu cod și pret, câmp de tip structurat informatii), 1p pentru declararea variabilei, 1p pentru corectitudinea globală a secvenței.
Subiectul al III-lea
Numărul natural an este ascendent al numărului natural n dacă oricare dintre cifrele lui an este mai mare sau egală cu cifra unităților lui n.
Exemplu: oricare dintre numerele 7, 9, 98 sau 7998 este ascendent al lui 827, dar numărul 857 nu este ascendent al lui 827.
Subprogramul ascendent are trei parametri:
n, prin care primește un număr natural ();xșiy, prin care primește câte un număr natural din intervalul ().
Subprogramul returnează suma ascendenților lui n din intervalul , sau valoarea 0, dacă nu există niciun astfel de ascendent. Scrieți definiția completă a subprogramului C/C++.
Exemplu: dacă n=827, x=9, y=800, subprogramul returnează 7893 ().
Rezolvare
Cifra de comparație este cifra unităților lui n, adică n%10; ea se calculează o singură dată. Apoi parcurgem intervalul și, pentru fiecare număr, îi verificăm toate cifrele cu ciclul standard c%10 / c/10.
int ascendent(int n, int x, int y)
{
int u = n%10, s = 0, i, c, ok;
for (i = x; i <= y; i++)
{
c = i;
ok = 1;
if (c == 0)
ok = (u == 0);
while (c > 0)
{
if (c%10 < u)
ok = 0;
c = c/10;
}
if (ok)
s = s+i;
}
return s;
}Două detalii care se pierd ușor:
1. numărul 0 nu intră în bucla while (c > 0), deci ar fi declarat ascendent din oficiu; el are o singură cifră, 0, deci este ascendent numai dacă cifra unităților lui n este tot 0;
2. dacă nu există niciun ascendent, suma rămâne 0, deci chiar valoarea cerută de enunț - nu este nevoie de un caz separat.
Verificare pe exemplu: pentru n=827 cifra unităților este 7, iar în numerele cu toate cifrele sunt cele din enunț, cu suma 7893.
Barem
Se cere definiția completă a unui subprogram C/C++ ascendent(n, x, y) care returnează suma numerelor din [x,y] ale căror cifre sunt toate ≥ cifra unităților lui n, sau 0 dacă nu există. Barem, total 10 puncte: antet al subprogramului 2p (câte 1p pentru structură și pentru parametrii de intrare); determinare a valorii cerute 6p (câte 1p pentru: identificarea cifrei unităților, compararea unei cifre cu o altă cifră, algoritm de bază pentru verificarea unei proprietăți, cifre suport verificate, algoritm de bază pentru determinarea sumei, numere ascendent însumate); instrucțiune de returnare 1p; declarare a variabilelor locale și corectitudine globală 1p.
Un cuvânt semioglindit se obține dintr-un cuvânt cu () litere, prin interschimbarea în acesta a secvenței formate din primele litere cu secvența formată din ultimele litere.
Exemplu: din cuvântul platim se obține cuvântul semioglindit timpla.
Într-un text de cel mult 200 de caractere, cuvintele sunt formate din litere mici ale alfabetului englez și sunt separate prin câte un spațiu. Scrieți un program C/C++ care citește de la tastatură un text de tipul precizat, pe care îl transformă în memorie, prin înlocuirea fiecărui cuvânt cu număr par de litere, cu cel semioglindit obținut din acesta, ca în exemplu. Programul afișează pe ecran textul obținut, sau mesajul nu exista, dacă toate cuvintele au număr impar de litere.
Exemplu: pentru textul am facut fotografii unei flori mari se afișează pe ecran textul ma facut rafiifotog eiun flori rima.
Rezolvare
Parcurgem textul cuvânt cu cuvânt, delimitând fiecare cuvânt între poziția lui de început și primul spațiu care urmează. Pentru un cuvânt de lungime pară , salvăm prima jumătate într-un tablou auxiliar, mutăm a doua jumătate în față și punem jumătatea salvată la sfârșit - transformarea se face în memorie, în același șir.
#include <iostream>
#include <cstring>
using namespace std;
char t[205], aux[105];
int main()
{
cin.getline(t, 205);
int n = strlen(t), i = 0, j, lg, k, p, ok = 0;
while (i < n)
{
j = i;
while (j < n && t[j] != ' ')
j++;
lg = j-i;
if (lg%2 == 0)
{
ok = 1;
k = lg/2;
for (p = 0; p < k; p++)
aux[p] = t[i+p];
for (p = 0; p < k; p++)
t[i+p] = t[i+k+p];
for (p = 0; p < k; p++)
t[i+k+p] = aux[p];
}
i = j+1;
}
if (ok)
cout << t;
else
cout << "nu exista";
return 0;
}Lungimea cuvântului nu se schimbă prin interschimbare, deci textul poate fi modificat pe loc, fără să fie nevoie de un al doilea șir pentru rezultat. Variabila ok reține dacă a existat măcar un cuvânt cu număr par de litere; dacă nu, se afișează mesajul cerut.
Barem
Se cere un program C/C++ care citește un text de cel mult 200 de caractere și înlocuiește în memorie fiecare cuvânt cu număr par de litere cu semioglinditul său (prima jumătate interschimbată cu a doua), afișând textul obținut sau mesajul nu exista dacă toate cuvintele au număr impar de litere. Barem, total 10 puncte: declarare a unei variabile care să memoreze un șir de caractere 1p; citire a datelor 1p; transformare a șirului 6p (câte 1p pentru: identificarea unui cuvânt cu număr par/impar de litere, caractere obținute în prima jumătate, caractere obținute în a doua jumătate, cuvinte înlocuite/păstrate, transformare în memorie, tratarea cazului nu exista); afișare a datelor 1p; declarare a variabilelor simple și corectitudine globală 1p. Citirea trebuie făcută cu cin.getline (textul conține spații).
Un tânăr pasionat de călătorii are o listă cu muzee virtuale și, pentru fiecare, câte un singur interval orar, în care acesta poate fi vizitat online, gratuit. Tânărul dispune zilnic de același interval orar pentru vizite; un muzeu este convenabil dacă poate fi vizitat online gratuit în timpul disponibil și dacă pentru vizită îi poate aloca cel puțin o oră. Muzeele din listă sunt numerotate cu valori naturale consecutive, începând cu 1, și cel puțin unul este convenabil.
Fișierul text bac.in conține cel mult linii, iar pe fiecare linie câte o pereche de numere, reprezentând limitele câte unui interval orar: pe prima linie intervalul orar de care tânărul dispune zilnic, iar pe fiecare dintre următoarele linii, intervalul orar de vizitare gratuită pentru câte un muzeu, în ordinea din listă. Limitele intervalelor sunt ore fixe, numere naturale din intervalul , iar cele aflate pe aceeași linie a fișierului sunt în ordine strict crescătoare și sunt separate printr-un spațiu.
Se cere să se afișeze pe ecran, separate printr-un spațiu, două valori, reprezentând numărul de muzee convenabile, respectiv numărul de ordine al ultimului astfel de muzeu din lista tânărului. Utilizați un algoritm eficient din punctul de vedere al timpului de executare și al memoriei utilizate.
Exemplu: dacă fișierul conține valorile 16 19, 15 18, 17 21, 19 21, 18 20, 12 13, atunci pe ecran se afișează numerele 3 4 (pot fi vizitate trei muzee cu numerele de ordine 1, 2 și 4, în intervalele 16-18, 17-19, respectiv 18-19).
Descrieți în limbaj natural algoritmul proiectat, justificând eficiența acestuia.
Rezolvare
Citim prima pereche - intervalul de care dispune tânărul - și apoi, pe măsură ce citim fiecare dintre perechile următoare , îi calculăm intersecția cu intervalul disponibil:
Dacă , adică intersecția conține cel puțin o oră, incrementăm contorul de muzee convenabile și reținem numărul de ordine curent ca fiind al ultimului muzeu convenabil. La final afișăm contorul și acest număr de ordine.
Eficiența: fiecare linie a fișierului este citită și prelucrată o singură dată, deci timpul de executare este liniar în numărul de muzee, - minimul posibil, pentru că orice soluție trebuie măcar să citească datele. Memoria folosită este constantă, : nu se rețin intervalele citite într-un tablou, ci doar intervalul disponibil, contorul, numărul de ordine curent și cel al ultimului muzeu convenabil. Cum fișierul poate avea până la linii, evitarea tabloului este exact ceea ce se cere prin „eficient din punctul de vedere al memoriei utilizate".
Barem
Se cere descrierea în limbaj natural a algoritmului eficient proiectat pentru problema muzeelor virtuale, cu justificarea eficienței. Barem, total 2 puncte: descriere coerentă a algoritmului 1p (se acordă chiar dacă algoritmul ales nu este eficient); justificare a elementelor de eficiență 1p. Soluția eficientă este liniară și cu memorie constantă: se citește prima pereche (h1,h2), apoi fiecare pereche următoare se prelucrează pe loc, calculând intersecția [max(h1,crt1), min(h2,crt2)].
Scrieți programul C/C++ corespunzător algoritmului proiectat.
Rezolvare
Programul citește din fișier pereche cu pereche, fără să memoreze nimic în plus:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream f("bac.in");
int h1, h2, c1, c2, v1, v2;
int nr = 0, ultim = 0, crt = 0;
f >> h1 >> h2;
while (f >> c1 >> c2)
{
crt++;
v1 = (h1 > c1) ? h1 : c1;
v2 = (h2 < c2) ? h2 : c2;
if (v2-v1 >= 1)
{
nr++;
ultim = crt;
}
}
f.close();
cout << nr << " " << ultim;
return 0;
}Pe exemplul din enunț, cu intervalul disponibil :
| muzeu | interval | intersecție | cel puțin o oră? |
|---|---|---|---|
| 1 | 15-18 | 16-18 | da |
| 2 | 17-21 | 17-19 | da |
| 3 | 19-21 | 19-19 | nu |
| 4 | 18-20 | 18-19 | da |
| 5 | 12-13 | vidă | nu |
Se afișează 3 4, ca în enunț.
Barem
Se cere programul C/C++ corespunzător algoritmului eficient descris la punctul a. Barem, total 8 puncte: operații cu fișiere - declarare, pregătire în vederea citirii, citire din fișier 1p; determinare a valorilor cerute 5p (numai 3p dacă algoritmul parcurge pașii necesari, dar cu detalii care conduc la o rezolvare parțială); utilizare a unui algoritm eficient 1p (se acordă numai pentru un algoritm liniar, care utilizează eficient memoria); declarare a variabilelor, afișare a datelor și corectitudine globală 1p.