Documente Academic
Documente Profesional
Documente Cultură
Recapitulare: Secvente de Caractere Complexitatea Algoritmilor Reguli Tipuri de Date
Recapitulare: Secvente de Caractere Complexitatea Algoritmilor Reguli Tipuri de Date
★ 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();
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.
while ( k > 1 ) {
n = n / 10;
k--;
}
cf = n % 10;
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.
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).
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
Exemple:
n = 90990900 → DA
n = 21323232 → NU
Exemple:
a = 2, n = 10 → 1024
a = 3, n = 7 → 2187
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;
}