Sunteți pe pagina 1din 4

Structuri de date

STL în concursuri
babel

Marius Andrei
În cadrul acestui articol vã vom prezenta una dintre facilitãþile oferite de
limbajul C++, care poate fi folositã în cadrul concursurilor de programare;
este vorba despre Standard Template Library.

STL (Standard Template Library) header-ele au extensia .h ºi cã name- aceeaºi cu cea din C/C++, (începând
privit foarte simplu, din punctul de space-ul utilizat trebuie sã fie std: de la indicele 0).
vedere al concurentului, este o colec- Dacã vrem sã adãugãm elementul
þie de structuri de date ºi algoritmi #include <iostream> 2 nu trebuie decât sã scriem
aplicaþi asupra acestora. #include <string> a.push_back(2), ºi l-am adãugat la
Evident cã este vorba de mult #include <vector> sfârºit.
mai mult decât atât, însã pentru sco- #include <set> Redimensionarea se realizeazã
pul articolului de faþã considerãm cã #include <map> prin a.resize(15). Astfel putem
evidenþierea acestei laturi este sufi- #include <algorithm> mãri sau micºora lungimea vectoru-
cientã. Pentru scopurile în care vom #include <queue> lui.
folosi noi STL-ul nu este nevoie de #include <assert.h> Lungimea vectorului ne este tot
cunoºtinþe avansate de programare using namespace std; timpul disponibilã printr-un apel de
orientatã obiect. forma a.size().
STL-ul se bazeazã pe câteva tem- Vectori Sortarea vectorului se realizeazã
plate-uri (ºabloane) care fac aplicabili Vom considera ca prim exemplu cre- folosind construcþia:
toþi algoritmii pentru orice tip de area unui vector ºi sortarea elemen- sort(&a[0],&a[N]); //N reprezintã
structuri de date. telor acestuia. Pentru a defini un vec- lungimea vectorului
Pentru a vã arãta impor- tor care conþine numere întregi se
tanþa STL-ului trebuie pre- poate folosi construcþia: Întrebarea evidentã aici se referã
cizat de la început faptul cã vector<int> a; la motivul pentru care cineva ar folosi
aceastã librãrie este stan- aceste clase, când poate face totul
dard. Toate exemplele au Nu existã nici o limitã asupra foarte simplu, folosind, de
fost testate atât cu gcc, dimensiunii vectorului. Ea exemplu, qsort.
GInfo nr. 14/6 - octombrie 2004

cât ºi cu Visual C++. poate fi precizatã de la în- Primul lucru specta-


Aºadar, librãria poate fi folositã ceput, însã nu este obliga- culos este cã sortarea
în concursuri, atât în cele pentru liceu toriu. De asemenea, putem ruleazã de pânã la 20 de
(olimpiade), cât ºi în cele studenþeºti preciza ºi o valoare iniþialã ori mai repede, în special
(ACM). Datoritã faptului cã librãria pentru elementele vectoru- datoritã implementãrilor
este relativ nouã, ea nu este disponi- lui respectiv: inline.
bilã în Borland C++ 3.1. vector<int> a(50,1); Întrucât uneori ne permitem
Prima informaþie de care avem de 20 de ori mai mult timp pentru o
nevoie pentru a putea utiliza STL În exemplul anterior este definit sortare, s-ar putea spune cã nu se
sunt fiºierele ce trebuie incluse. Inclu- un vector a care conþine 50 de ele- câºtigã prea mult, dar mai e mult de
dem mai mult decât ne este necesar, mente care sunt iniþializate cu valoa- învãþat de la STL.
însã aceste header-e ne vor folosi mai rea 1. Funcþia sort are doi parametri:
încolo, în exemplele urmãtoare. Accesarea unui element se reali- primul element ºi elementul imediat
Veþi observa cã în partea de înce- zeazã, aºa cum suntem obiºnuiþi, urmãtor ultimului element.
put a programului nu apare nimic folosind construcþia a[i], pentru al De obicei se scrie:
38 anormal, în afarã de faptul cã nu toate i-lea element, numerotarea fiind sort(a.begin(), a.end()),
însã pentru a înþelege aceastã con- Determinarea valorii elementului vector de întregi infinit de lung (la
strucþie trebuie sã ºtim câte ceva des- cu cea mai mare prioritate (elementul ambele capete).
pre iteratori. din vârful heap-ului) se realizeazã cu Deja începem sã ne dãm seama de
ajutorul funcþiei top() specificã struc- câteva dintre avantajele STL-ului.
Iteratori turii de date:
Trebuie sã ne gândim la iteratori ca int varf = a.top(); Mulþimi
fiind pointeri "in- Poate cel mai folositor lucru pentru
teligenþi", Extragerea elementului cu cea un concurent sunt mulþimile. Ele se
folosiþi mai mare prioritate se realizeazã cu declarã folosind o construcþie de

babel
pentru a ne deplasa ajutorul funcþiei pop() specificã tipul:
printre structurii de date: set<int> a;
elementele structurii noastre de date a.pop();
(pentru toate tipurile de structuri, nu Operaþiile de bazã cu mulþimile
doar pentru vectori). Existã susþinãtori ai ideii potrivit sunt inserarea, ºtergerea ºi cãutarea
De exemplu, declararea unui cãreia implementarea unui heap este de elemente care se pot realiza cu
iterator pe o structurã de tip vector imediatã ºi este preferabil sã avem ajutorul funcþiilor insert(),
de numere întregi se poate face cu control total asupra structurii de date erase() ºi find():
ajutorul construcþiei: pe care o implementãm. a.insert(7);
vector<int>::interator i;. Existã argumente în favoarea a.erase(5);
acestei idei dar, din alt punct de vede- if (a.find(7) != a.end())
Pentru aceste structuri de date re, ar putea fi de preferat sã ºtim si- cout << Gasit;
existã doi iteratori impliciþi ºi anume gur cã nu vom greºi ºi cã vom mai
begin() ºi end(), care permit obþi- câºtiga cîteva minute care se pot do-
nerea unui iterator pentru primul ele- vedi preþioase.
ment, respectiv pentru elementul de
dupã ultimul element. Tabele de dispersie
Pe parcurs vom prezenta mai Cu ajutorul acestor tabele se repre-
multe detalii referitoare la folosirea zintã o relaþie 1 la 1 între douã tipuri
iteratorilor. de date. Primul element este cheia,
iar al doilea este valoarea. În
Cozi de prioritãþi exemplul urmãtor vom asocia câte
Declararea unei cozi de prioritãþi un numãr întreg fiecãrui oraº:
(heap) se realizeazã cu ajutorul unei map<string,int> a;
construcþii de forma: a["Bucuresti"] = 10;
priority_queue<int> a; a["Cluj"] = 7;
a["Iasi"] = 3; Pe lângã aceste operaþii de bazã,
De obicei implementarea unei a["Constanta"] = 8; sunt definite intersecþia, reuniunea ºi
cozi de prioritãþi se face cu ajutorul diferenþa.
heap-urilor. Dacã dorim sã tipãrim numã- Implementarea ope-
Introducerea de elemente într-o rul corespunzãtor Clujului vom raþiilor cu mulþimi nu GInfo nr. 14/6 - octombrie 2004
coadã de prioritãþi se realizeazã scrie: mai pare a fi foarte
folosind o construcþie similarã cu: cout << a["Cluj"] simplã, iar una
a.push(8); cu arbori echi-
Efectul acestei construcþii libraþi în mod sigur
Determinarea dimensiunii cozii va consta în afiºarea valorii 7 la nu e facilã, mai ales
de prioritãþi se face cu ajutorul ieºirea standard. în momentul în care trebuie imple-
funcþiei size() specificã structurii de Dacã nu existã cheia, se va afiºa mentat algorimtul de ºtergere a unui
date: valoarea 0 (în general, se va lua în nod.
int dim = a.size(); considerare constructorul implicit În plus, orice operaþie are ordinul
dacã este vorba de alt tip de datã de complexitate O(log N)!
decât numãr întreg) . Pe lângã timpul câºtigat (puþini se
Astfel, a["Timisoara"] va avea pot lãuda cu numai 20 de minute
valoarea 0. pentru a implementa un arbore AVL
Este interesant de menþionat cu ºtergeri), nu trebuie sã ne facem
faptul cã structura de date creatã prin griji cã ar putea exista greºeli de im-
map<int, int> poate fi privitã ca un plementare.
39
Folosirea iteratorilor
Pentru exemplificare vom parcurge
elementele unei mulþimi ºi elemen-
tele unei tabele de dispersie.

set<int> a;
map<string,string> b;

for(set<int>::iterator i =
babel

a.begin(); i != a.end(); ++i)


cout << *i << endl;
for(map<string,string>::
iterator i = b.begin();
i != b.end(); ++i)
cout << i->first << " => " <<
i->second << endl;

Este necesar sã folosim construc-


þia "!= a.end()" ºi nu "<
a.end()", deoarece datele nu sunt
obligatoriu ordonate în memorie.

make_pair
Toate exemplele de pânã acum utili-
zeazã numere întregi. Aºa cum am a.push_back(make_pair(x,y)); doar sã implementãm mai repede
mai zis, putem pune orice în loc de // make_pair creeazã o clasã rezolvarea.
întregi, orice structurã sau clasã. Pen- // pair<int,int> Dar în concursuri, faþã de cei care
tru aceasta, este suficient sã definim } folosesc limbajul Pascal, STL oferã
operatorul "<". sort(a.begin(),a.end()); un avantaj fãrã discuþii. Deºi nu mai
bool operator< (const MyStruct //sorteazã dupã prima coordonatã sunt foarte mulþi programatori Pas-
&a, const MyStruct &b) //(primul întreg), cal, aceºtia existã, mulþi dintre ei fiind
{ //iar în caz de egalitate dupã a doua chiar foarte buni. Aceºtia vor acuza
// returneazã true dacã a<b, //coordonatã. folosirea STL-ului ºi dintr-un anumit
// false dacã a>=b punct de vedere au dreptate. Proble-
} Mult mai mult mele propuse sunt în general alese în
Tot ce am prezentat este doar o micã aºa fel încât sã ofere ºanse egale atât
Existã o clasã parte din facilitãþile oferite de STL. programatorilor în C++, cât ºi celor
predefinitã care Pentru o prezentare completã vã care programeazã în Pascal. Acum
nu face decât sã recomandãm sã accesaþi adresa apare un avantaj în favoarea celor
încapsuleze http://www.sgi.com/tech/stl/. care aleg limbajul C++.
GInfo nr. 14/6 - octombrie 2004

douã date Este clar cã folosirea STL-ului Totuºi, nu putem interzice folosi-
(oricare douã reprezintã un avantaj. Timpul de im- rea STL-ului deoarece aceastã librãrie
date) ºi anume clasa pair. plementare este redus, timpul de ru- este un standard.
Ca exemplu, vom prezenta o sec- lare este mai mic, posibilatea apariþiei STL-ul este o evoluþie, însã nici
venþã în care vom declara un vector erorilor în structura noastrã de date nu trebuie privit ca un element care
ale cãrui elemente sunt perechi de nu- este inexistentã. trebuie obligatoriu cunoscut.
mere întregi ºi vom sorta elementele Unii concurenþi ar putea sã pro- Pentru a-l utiliza în concursuri,
vectorului respectiv. testeze împotriva folosirii STL-ului. este necesarã o cunoaºtere bunã a
Vor spune cã nici nu mai trebuie sã librãriei, pentru cã altfel apare riscul
int N,x,y; ºtii arbori AVL (sau arbori roºu- de a o folosi incorect sau de a pierde
vector< pair<int,int> > a; negru), nici hash-uri, nici mãcar un timp cu folosirea sa.
//este esenþial sã existe un spaþiu heap. Vom deveni simplii utilizatori De asemenea, trebuie menþionat
//între ">" ºi ">"! ai structurilor, în loc sã le putem faptul cã depanarea programelor care
cin >> N; implementa de fiecare datã. Poate e folosesc STL este foarte dificilã, iar la
for(int i=0;i<N;++i) { adevãrat, însã STL-ul nu ne ajutã sã compilare vor apãrea o mulþime de
40 cin >> x >> y; gãsim metoda de rezolvare. Ne ajutã avertismente legate de numele prea
lungi ce vor fi trunchiate la 255 de a vaporului corespunzãtor liniei (mai set< pair<int,int> >::iterator
caractere. exact, pe linia i din fiºier sunt scrise co- It, It2, t;
ordonatele vaporului identificat prin i
Un exemplu complet - 1, pentru orice valoare i cuprinsã int main() {
La final vom prezenta un exemplu între 2 ºi N + 1). FILE *fi = fopen("sea2.in",
complet de folosire a STL-ului. "rt");
Problema a fost propusã spre re- Date de ieºire FILE *fo = fopen("sea2.out",
zolvare în tabara de pregãtire de la Fiºierul de ieºire sea2.out va conþi- "wt");
Orãºtie. Enunþul ºi rezolvarea îi ne N linii, pe fiecare dintre acestea int x, y, nr;

babel
aparþin lui Radu Berinde. aflându-se câte un numãr întreg.
Pe linia i se va afla va- for (fscanf(fi, "%d", &nr);
Enunþ loarea -1 dacã vaporul i este nr; nr--) {
Pe mare va avea loc o distrus în momentul apa- fscanf(fi, "%d %d", &x, &y);
mare bãtãlie între N riþiei, sau un numãr în- It = Ships.upper_bound(
vapoare. Vapoarele treg strict pozitiv re- make_pair(x, y));
sunt considerate a fi prezentând numãrul if (It != Ships.end() &&
puncte ºi sunt date de vapoare de pe It->second > y) {
prin coordonatele lor mare dupã apariþia fprintf(fo, "-1\n");
carteziene x ºi y. vaporului i în caz contrar. continue;
Din motive greu de înþeles, va- }
poarele nu pot ataca decât vapoarele Restricþii ºi precizãri if (Ships.size()) {
care se aflã la stânga ºi mai jos (mai • 1 ≤ N ≤ 200.000; for (It2 = It;
exact, un vapor la poziþia x1, y1 poate • coordonatele sunt numere întregi It2 != Ships.begin() &&
ataca alt vapor la poziþia x2, y2 dacã ºi strict pozitive mai mici sau egale cu (--(t = It2))->
numai dacã x1 > x2 ºi y1 > y2). 260000; second < y;
Deoarece aceastã bãtãlie are loc • nu vor exista douã vase cu aceeaºi It2 = t
în zona Triunghiului Bermudelor, coordonatã x sau cu aceeaºi coor- );
vapoarele apar (se teleporteazã) pe donatã y. Ships.erase(It2, It);
rând în zona bãtãliei. Vapoarele sunt }
identificate prin numere întregi Soluþie Ships.insert(
cuprinse între 1 ºi N, în ordinea #include <stdio.h> make_pair(x, y));
apariþiei lor. #include <stdlib.h> fprintf(fo, "%d\n",
În momentul în care un vas apa- #include <vector> Ships.size());
re, dacã existã alt vas care a apãrut #include <set> }
deja ºi care poate sã îl atace pe cel #include <utility> fclose(fo);
nou, vasul nou este distrus instanta- using namespace std; fclose(fi);
neu. Dacã nu, vasul cel nou rãmâne return 0;
pe mare ºi distruge toate vasele pe set< pair<int,int> > Ships; }
care le poate ataca.
Dându-se coordonatele la care GInfo nr. 14/6 - octombrie 2004
apar pe rând vapoarele, sã se afle
pentru fiecare vapor dacã este distrus
sau nu în momentul apariþiei sale ºi
dacã nu este distrus, sã se precizeze
numãrul total de vapoare rãmase pe
mare dupã apariþia sa.

Date de intrare
Pe prima linie a fiºierului de intrare
sea2.in se aflã numãrul natural N
reprezentând numãrul de vapoare ce
vor apãrea pe mare.
Pe fiecare dintre urmãtoarele N
linii se aflã câte o pereche de numere
întregi separate printr-un spaþiu, re-
prezentând coordonata x respectiv y
41

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