Sunteți pe pagina 1din 21

Recapitulare

★ Secvente de caractere
★ Complexitatea algoritmilor
★ Reguli
★ Tipuri de date
Recapitulare - Secvente de caractere
● in C++, caracterul endline este reprezentat prin ‘\n’ (acest caracter este unul special, de aceea
contine cu acel backslash = \);
● daca citirea se face din fisier si presupunem numele fisierului de intrare fin, atunci putem citi orice
tip de caracter folosind fin.get(c), unde c este o variabila de tip caracter;
● pentru a citi din fisier pana la intalnirea ‘\n’, vom folosi while (sa testam in C++):

ifstream fin;
char c;

fin.open("test.in");
fin.get(c);
while (c!='\n') {
/// prelucrarea lui c
fin.get(c);
}
fin.close();
Recapitulare - Secvente de caractere
Pentru afisarea unor caractere intr-un fisier, putem folosi o metoda optima de afisare, diferita de clasicul:

ifstream fin;
ofstream fout;
char c;
fin.open("test.in");
fin>>c;
fin.close());
fout.open("test.out");
fout<<c;
fout.close();

Vom prefera utilizarea:


ifstream fin;
ofstream fout;
char c;
fin.open("test.in");
fin.get(c);
fin.close());
fout.open("test.out");
fout.put(c);
fout.close();
Recapitulare - Secvente de caractere
1. Din fisierul “sbvoc.in” se citesc caractere pana la intalnirea caracterului endline/enter (‘\n’). In
fisierul “sbvoc.out” se va afisa lungimea celei mai lungi subsecvente de caractere de tip vocala (se
considera vocale atat literele mari de tipar, cat si cele mici).
Obs: Daca intre vocale exista spatii, ele vor fi ignorate atat dpdv al formarii subsecventei, cat si dpdv
lungime maxima.

Exemplu: a, EiuT o iU a &4iO l;


se va afisa 4 (cea mai lunga subsecventa este data de “o iU a” si contine 4 vocale)

Logica de rezolvare:
- se citeste un caracter c
- lc = 0
- lmax = 0
- cat timp c != ‘\n’ executa
- daca c == ‘a’ || c == ‘e’ || c == ‘i’ … c == ‘A’ ..
- lc++
- altfel daca c != ‘ ‘ atunci
- lc = 0
- daca lc > lmax atunci
- lmax = lc
- se citeste un caracter c
- se afiseaza lmax
In C++
#include <fstream>
using namespace std;
int main() {
ifstream fin;
ofstream fout;
char c;
int lc, lmax;
fin.open("sbvoc.in");
fin.get(c);
lc = 0;
lmax = 0;
while (c != '\n') {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
lc ++;
}
else if (c != ' ') {
lc = 0;
}
if (lc > lmax) {
lmax = lc;
}
fin.get(c);
}
fin.close();

fout.open("sbvoc.out");
fout<<lmax;
fout.close();

return 0;
}
Recapitulare - Secvente de caractere
2. Din fisierul “litcresc.in” se citesc caractere pana la intalnirea caracterului endline. In fisierul
“litcresc.out” se vor afisa, in ordinea crescatoare a numarului de aparitii literele regasite in secventa de
caractere (vom face abstractie de “dimensiunea” literei → “a” va incrementa nr de aparitii a literei, dar si
“A” va face acelasi lucru; putem spune ca suntem “CASE INSENSITIVE” in cadrul acestei probleme).
Obs: Se vor afisa litere mici, indiferent daca s-au regasit numai litere mari sau combinatii.
Exemplu: Yay! Noi suntem la cursul nivel 3 de informatica 2020! SUCCES TUTUROR!

Se va afisa: d f v m y l o a c e i n r s t u

Deoarece:
Lit y a n o i s u t e m l c r v d f

Nr 2 4 4 3 4 4 6 4 4 2 3 4 4 1 1 1

Logica de rezolvare:
- pentru i = 1; i <= nrap;; i++
- se citeste caracterul c - pentru j = 0; j < 26; j++
- cat timp c != ‘\n’ executa - daca v[j] == i atunci
- daca c >= ‘a’ && c <= ‘z’ atunci - se afiseaza ‘a’+j
- v[c-’a’]++
- daca c >= ‘A’ && c <= ‘Z’ atunci
- v[c-’A’]++
- pentru i= 0; i < 26; i++
- daca v[i] > nrap atunci
- nrap = v[i]
In C++
#include <fstream>
if (c >= 'A' && c <= 'Z') {
using namespace std; v[c-'A']++;
if (v[c-'A'] > nrap) {
int v[26]; nrap = v[c-'A'];
}
int main() { }
ifstream fin; fin.get(c);
ofstream fout; }
fin.close();
int i, j, nrap;
char c; fout.open("litcresc.out");
for (i = 1; i <= nrap; i++) {
fin.open("litcresc.in"); for (j = 0; j < 26; j++) {
fin.get(c); if (v[j] == i) {
nrap = 0; fout<<(char)(j+'a')<<" ";
while (c != '\n') { }
if (c >= 'a' && c <= 'z') { }
v[c-'a']++; }
if (v[c-'a'] > nrap) { fout.close();
nrap = v[c-'a'];
}
} return 0;
}
Recapitulare - complexitati
Complexitate unui aloritm este relevanta in momentul in care vrem sa decidem care algoritm dintr-un
set de aloritmi echivalenti este cel mai eficient (Def: Doi algoritmi sunt echivalenti daca pentru
acelasi set de date de intrare se raspude cu aceleasi date de iesire).
Eficienta algoritmilor poate fi determinata lunad in considerare mai multe puncte:
● Memoria folosita (ne referim atat la numarul de variabile folosite, cat si la dmensiunile
variabilelor folosite; de asemenea, ne referim si la memoria stiva folosuta in cadrul unor
algoritmi recursivi etc.);
● Timpul de executie (numarul de secunde in care se executa programul luand valorile cele mai
mari pentru datele de intrare);
● Numarul de pasi executati.

Desi punctele mentionate mai sus sunt destul de clare, pot aparea diferite probleme:
● calculatoarele pe care se executa programele pot fi diferite → rezultatele dpdv eficienta pot
fi diferite;
● sistemul de operare al unui calculator poate fi diferit → rezultatele dpdv eficienta pot fi
diferite;
● compilatorul utilizat poate fi diferit (un compilator pe Linux pt limbajul C vs unul pe
Windows pt limbajul C++ poate fi mai eficient) → rezultatele dpdv eficienta pot fi diferite.

Din motivele enumerate mai sus, s-a luat decizia de a estima complexitatea unui aloritm folosind
notatia O mare si notata O().
Recapitulare - complexitati
Folosin O(), vom estima numarul de pasi elementari executati de un algoritm atunci cand datele sunt
foarte mari.
Astfel, nu depindem de eficienta implementarii si nici de calculatorul pe care se va executa acel
algoritm. De asemenea, nu putem vorbi despre eficienta cand datele au valori foarte mici pt ca,
implicit, timpul de executie va fi mic.

Exemplu 1: Determinati cifra k a unui numar n, numarand cifrele de la dreapta la stanga.


Pt n = 234567 si k = 4, se va afisa 4 (deoarece a 4-a cifra de la dreapta la stanga este 4 → 234567
)

Un algoritm de rezolvare este:

while ( k > 1 ) {
n = n / 10;
k--;
}
cf = n % 10;

Cat credeti ca este complexitatea (numarul de pasi elementari executati)?


Raspuns: O(k).
Recapitulare - complexitati
Exemplu 2: Determinati descompunerea in factori
primi a numarului n. #include <iostream>
using namespace std;
Pt n = 6860, se va afisa:
int main() {
2^2 int n, div, exp;
5^1
7^3 cin>>n;
div = 2; //cautam divizori de la primul nr prim
while ( div * div <= n ) { //ultimul factor prim posibil
Dpdv complexitate: este radical din n
● bucla exterioara se executa de maxim exp = 0;
sqrt(n) ori; while ( n % div == 0 ) {
n /= div;
● in cazl buclei interioare, daca se executa
exp++;
de mai multe ori scade nr total de }
operatii (de ce?); if (exp > 0) // daca am gasit cel putin un facot, il
● cel mai “nefavorabil” caz este pt n prim → afisam
cout<<div<<"^"<<exp<<endl;
bucla interioara nu se executa.
div++;
}
Asadar, numarul de pasi elementari executati // daca n nu a ramas 1, este numar prim
este proportional cu sqrt(n) → complexitatea = if ( n > 1 )
cout<<n<<"^1";
O(sqrt(n)).
return 0;
}
Recapitulare - complexitati
Notatia O() ia in calcul proportionalitatea numarului de pasi executati cu datele de intrare,
asadar ignora constantele multiplicative si aditive.

De exemplu:
● 1 pas = complexitate O(1);
● 25 de pasi = complexitate O(1), deoarece se ignora constanta 25;
● 3*n pasi = complexitate O(n), deoarece se ignora constanta 3;
● 3*n+8 pasi = complexitate O(n), deoarece se ignora constantele 3 si 8.

Complexitati pt algoritmi cunoscuti:


● Adunarea a doua numere: O(1);
● Impartirea a doua numere: O(1);
● Calcului sumei de la 1 la n, folosind suma Gauss: O(1);
● Calcului sumei de la 1 la n, folosind “forta bruta” (ce credeti ca inseamna?): O(n);
● Ridicarea la putere a^b: O(b);
● Sortarea prin selectie: O(n^2);

Obs: Credeti ca un algoritm cu complexitate O(n) este mai bun decat unul de complexitate
O(sqrt(n))? Justificati raspunsul.
Recapitulare - complexitati
Cele mai multe concursuri / olimpiade va cer sa va incadrati intr-un anumit timp de executie si nu
intr-o anumita complexitate, ceea ce inseamna ca va trebui sa invatam sa ne calculam si acesti
timpi pt algoritmi.
Dupa cum spuneam anterior, timpul de executie depinde de calculator, dar, de obicei, un procesor
are o frecventa de aproximativ 1-2Ghz → poate executa intre 1 si 2 miliarde de operatii elementare
intr-o secunda. (obs: o operatie elementara a algoritmului este diferita de o operatie elementara a
calculatorului, vom discuta despre acest lucru).

Cum ne dam seama daca ne incadram in timp:


● Estimam complexitatea algoritmului; sa zicem ca avem un algoritm de O(n^2);
● Inlocuim valorile de intrare cu cele maxime date in problema; sa zicem ca n<10.000 → n^2 =
100.000.000 (100 milioane);
● Comparam cu timpul de executie cerut in problema:
○ daca timpul este 1s, atunci suntem acoperiti pt ca se pot executa cateva sute de
milioane de operatii intr-o secunda;
○ daca timpul este 0.1s, atunci riscam sa depasim timpul, deoacere nu ne putem baza decat
pe cateva zeci de milioane de operatii in timpul alocat.
Recapitulare - complexitati
Pentru a intelege mai bine cat de “costisitoare” este fiecare operatie, vom discuta despre cele mai
utilizate operatii raportate la adunare (adunarea este cea mai putin costisitoare si s-ar incadra
la capitolul “operate elementara a calculatorului”):
● adunare, scadere, inmultire = o adunare;
● impartire si modulo ~= 30 de adunari;
● impartire la 2 si modulo 2 = o adunare (aceasta este o exceptie de la impartiri/modulo
uzuale);
● radical (sqrt) ~= sute de adunari;
● ridicare la putere (pow), logaritm ~= sute de adunari

In concluzie:
● este bine sa evitam impartirile de care nu avem nevoie, mai ales daca apar in numar mare;
● atunci cand avem multe impartiri, numarul de operatii permis trebuie impartit la 30 pt
impartiri / la 500-1000 pt radical;
● preferam sa folosim pe cat posibil alte modalitati de calcul in loc de radical (de exemplu,
pentru problema precedenta, in loc de div<=sqrt(n), am folosit div*div<=n).

In continuare vom lua ca exemplu cateva probleme rezolvate pt care sa determinam complexitatea.
Recapitulare - complexitati
Secventa monotona: Se citesc n si o secventa de n numere naturale. Sa se spuna daca secventa este
monotona (o secventa este monotona daca este fie in ordine crescatoare, fie in ordine
descrescatoare). !! Rezolvare fara vectori !!

Exemple:
n = 5 si secventa 2 2 2 2 2 → DA
n = 5 si secventa 1 1 1 1 2 → DA
n = 5 si secventa 4 4 3 3 2 → DA
n = 5 si secventa 2 2 3 3 2 → NU

Logica de rezolvare:
-
Recapitulare - complexitati
#include <iostream>
using namespace std; if (monoton == cresc) {
int main() { cout<<"DA";
int n, a, b, i, cresc, monoton; }
cin>>n>>a>>b; // n si primele doua numere else {
i = 2; cout<<"NU";
// Ignoram toate elemente egale de la inceput, pt a stabili monotonia sirului }
while (i < n && a == b) { return 0;
cin>>b; }
i++;
}
Ce complexitate avem in cazul acestei
// Daca sirul este crescator, monoton = 1; Altfel, monoton = -1.
cresc = monoton = (a < b) ? 1 : -1; // stiti ce reprezinta?
solutii?
// Cat timp nu am terminat si este pastrata monotonia initiala
while (i < n && monoton == cresc) { O(n)
a = b;
cin>>b;
if (a < b) {
cresc = 1;
}
else if (a > b) {
cresc = -1;
}
i++;
}
Recapitulare - reguli
● Declararea vectorilor se va face in afara programului principal (f util pt vectorii de
frecventa);
● Prima pozitie din vector este 0 si nu 1;
● Initializarea variabilelor se face cat mai aproape de locul in care urmeaza sa fie folosita
variabila respectiv si NU la declarare;
● Codul va fi identat corect in functie de cum aveti setarile in codeblocks → sa nu vedem ceva
de tipul (ideal ca identarea sa fie la 2 sau 4 spatii):
if (a>b)
a=2;
else
a=5;
● Pentru a citi mai usor codul, folositi acolade chiar daca aveti o singura instructine de
executat (exemplu negativ mai sus si din acest punct de vedere);
● Acolada deschisa se pune pe aceeasi linie cu instructiunea de care apartine;
● Acolada inchisa se pune pe randul urmator, aliniata cu instructiunea de care apartine;
● Fara break, continue, return, goto → folosim for pt nr cunoscut de pasi si while altfel;
● Nu se modifica niciodata variabilele utilizate in for (de aceasta folosim for cand avem nr
cunoscut de pasi...);
● Puneti comentarii in cod mai ales atunci cand stiti ca aveti un cod destul de complex - va
ajuta si pe voi, dar si pe mine cand verificam temele;
● Numiti variabilele cat mai clar - a,b,c nu imi spun nimic, spre deosebire de nrap, sum, prod.
Recapitulare - Tipuri de date intregi

Tip de date Dimensiune De la - pana la

char 1 octet (8 biti) → 2^8 = -127 .. 128


256

int 4 octeti (32 biti) → 2^32 nr de ~ 10 cifre (-2 miliarde .. 2 miliarde)


= 4.294.967.296

long long 8 octeti (64 biti) → 2^64 ~18/19 cifre

● putem sa folosim unsigned pt a mai creste un pic nr valorilor, doar daca


nu avem nevoie de valori negative;
● tipul de date bool pare ca ocupa mai putin spatiu in memorie, insa el
ocupa fix tot atat cat ocupa si char
Tema - complexitati
1. Spuneti daca n citit de la tastatura este format din exact doua cifre repetate (n <
2.000.000.000). !! Rzolvare fara vectori !!

Exemple:
n = 90990900 → DA
n = 21323232 → NU

Dupa ce rezolvati, determinati complexitatea algoritmului


Tema - complexitati
2. Se citesc a si n, numere naturale, de la tastatura. Sa se calculeze cat mai eficient a la
puterea n si s se afiseze.

Exemple:
a = 2, n = 10 → 1024
a = 3, n = 7 → 2187

Dupa ce rezolvati, determinati complexitatea algoritmului


Tema- complexitati
3. Paranteze: Se citesc n si o secventa de 0-uri si 1-uri, unde 0 reprezinta o paranteza deschisa
‘(’ si 1 reprezinta o paranteza inchisa ‘)’. Sa se spuna daca secventa de paranteze este corecta
si, daca da, sa se spuna si factorul de imbricare (numarul de paranteze interioare). !! Rezolvare
fara vectori !!

Exemple:
n = 8 si secventa 0 1 0 0 1 0 1 1 se va afisa:
DA
2
Obs: Nivelul de imbricare este 2 pt ca, in traducere am avea ()(()())

n = 8 si secventa 0 0 0 1 1 1 0 0 se va afisa:
NU

Idei de rezolvare?
Rezolvare
#include <iostream> Ce complexitate avem in cazul acestei
using namespace std;
solutii?
int main() {
int n, a, i, nrdesc, max;
cin>>n;
max = nrdesc = i = 0;
while ( i < n && nrdesc >= 0 ) {
cin>>a;
if ( a == 0 ) {
nrdesc++;
if ( nrdesc > max ) {
max = nrdesc;
}
} else {
nrdesc--;
}
i++;
}
if ( nrdesc == 0 ) {
cout<<"DA"<<endl<<max;
}
else {
cout<<"NU";
}
return 0;
}

S-ar putea să vă placă și