Seminar
Seminar
Seminar 1 - Input/Ouput
Input si output pentru o problema data:
- input = datele de intare, ce se da;
- output = datele de iesire, ce se cere;
1. primele n nr date de un nr n;
Input: n € N;
Ouput: a1, a2, …, an, ai € N, Pirm(ai) ^ ai < ai+1, oricarearfi i =1,n
Prim(x) = oricarearfi d( (d>1 ^ d<x)→(x%d !=0)
def este_prim(numar):
if numar < 2:
return false
for i in range(2, int(numar**0.5) +1):
if numar % i == 0;
return flase
return true
def primele_n_nr_prime(n):
numere_prime = []
numar = 2
while len(numere_prime) < n:
if este_prim(numar):
numere_prime.append(numar)
numar += 1
print (numere_prime)
lista f(int n) {
if(n==0) {
return [Link];
}
bool a[1000] = {0};
ciur(a[], n);
[Link](a);
for(int i=3; i<=n; i++){
if(a[i]==0) [Link](i);
return lista;
}
}
1. un vector, doi indici pentru cele mai mici doua nr din vector consective
a. Input: Un vector V de numere naturale cu cel puțin două elemente.
Fie V=[v0,v1,...,vn−1], unde vi∈N, i∈[0, n-1] și n≥2.
Output: i, j unde ai <= aj, oricare k != i,j avem ak>=aj, i,j,k∈0,n-1, i!=j
b. Corectitudine algoritm
Inițializarea corectă a celor mai mici două elemente
• Primele două elemente sunt comparate și se inițializează sml (indicele celui mai mic
element) și nxtsml (indicele celui de-al doilea cel mai mic element).
• Aceasta este o metodă validă și corectă.
Parcurgerea vectorului și actualizarea indicilor
• Se parcurge vectorul de la al treilea element (i = 2) până la n - 1.
• Dacă a[i] este mai mic decât a[sml], atunci sml trebuie actualizat, iar vechiul sml devine
nxtsml.
Problemă: Algoritmul nu verifică dacă a[i] este mai mare decât a[sml], dar mai mic decât
a[nxtsml]. Dacă a[i] nu este cel mai mic, dar este al doilea cel mai mic, acesta trebuie să
actualizeze nxtsml.
Lipsa actualizării corecte pentru nxtsml
• Dacă a[i] nu este cel mai mic, dar este mai mic decât a[nxtsml], nxtsml ar trebui să fie
actualizat, dar algoritmul NU face acest lucru.
• De exemplu, pentru inputul [3, 5, 2, 7, 4, 1], algoritmul va găsi corect sml = 5, dar nu va
actualiza corect nxtsml = 2.
2. Input: A=(a0, a1, … an-1), ai∈Z, i=0,n-1, oricare i,j, ai<aj
Output: i, 0<=i<=n-1, ai==i, daca exista un astfel de element
-1, daca pt oricare i=0,n-1 avem ai!=i
def gaseste_punct_fix(arr):
st, dr = 0, len(arr) - 1
if arr[mid] == mid:
return mid
elif arr[mid] < mid:
st = mid + 1
else:
dr = mid - 1
for i in range(max_len):
cifra_a = int(a[i]) if i < len(a) else 0
cifra_b = int(b[i]) if i < len(b) else 0
if carry:
[Link](str(carry))
for i in range(len(a)):
for j in range(len(b)):
produs = int(a[i]) * int(b[j])
rezultat[i + j] += produs
rezultat[i + j + 1] += rezultat[i + j] // 10 # Transport
rezultat[i + j] %= 10
# Eliminăm zerourile din față
while len(rezultat) > 1 and rezultat[-1] == 0:
[Link]()
5. Problema sortarii
Input: A=[a1,a2,…,an] – un șir de n numere naturale.
Ouput: A′=[a1′,a2′,…,an′], astfel incat a1′≤a2′≤⋯≤an′
a) Booble sort
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False # Optimizare: verificăm dacă au fost schimbări
for j in range(0, n - i - 1): # Ultimele i elemente sunt deja sortate
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j] # Swap
swapped = True
if not swapped: # Dacă nu s-au făcut schimbări, lista este deja sortată
break
return arr
Pasul de inducție
Presupunere de inducție: Să presupunem că după k parcurgeri, cele mai mari k elemente sunt
plasate corect în ultimele k poziții ale vectorului.
Dovedim pentru k + 1:
• La a (k+1)-a parcurgere, aplicăm aceeași strategie: comparăm și interschimbăm perechi
adiacente.
• Elementul cel mai mare dintre cele rămase se „ridică” spre dreapta și ajunge pe poziția
corectă (n - k - 1).
• Astfel, acum primele n - k - 1 elemente rămase sunt nesortate, iar ultimele k+1 elemente
sunt în poziția finală corectă.
3. Terminarea algoritmului
Trebuie să demonstrăm că algoritmul se oprește și că la final vectorul este complet sortat.
• În cel mai rău caz, avem nevoie de n-1 parcurgeri pentru ca toate elementele să fie în ordine
corectă.
• După n-1 parcurgeri, toate elementele sunt sortate, deci nu mai sunt necesare interschimbări
(swapped = False → bucla se oprește).
• Deoarece n-1 este un număr finit, algoritmul se termină întotdeauna.
4. Concluzie
1. Prin inducție, am demonstrat că după k parcurgeri, cele mai mari k elemente sunt plasate
corect.
2. După n-1 parcurgeri, toate elementele sunt ordonate în ordine crescătoare.
3. Algoritmul se termină într-un număr finit de pași, deci este corect.
Selection sort
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i # Considerăm inițial că elementul curent este cel mai mic
for j in range(i + 1, n):
if arr[j] < arr[min_idx]: # Căutăm cel mai mic element
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i] # Swap
return arr
Algoritmul rezolva corect problmea?
1. Definim invariantul buclei
După k iterații ale buclei externe, primele k elemente din vector sunt sortate în ordine
crescătoare și conțin k dintre cele mai mici elemente ale vectorului inițial.
Pasul de inducție
Presupunem că invariantul este adevărat pentru k iterații, adică primele k elemente sunt
sortate și conțin cele mai mici k valori.
Dovedim că este adevărat și pentru k + 1:
1. În a k+1-a iterație, algoritmul caută cel mai mic element din secțiunea nesortată (de la
poziția k la n-1).
2. Îl mută în poziția k, asigurându-se că primele k+1 elemente sunt acum sortate și conțin cele
mai mici k+1 valori.
3. Restul elementelor rămân nesortate, dar toate sunt mai mari decât primele k+1 elemente.
3. Terminarea algoritmului
Trebuie să demonstrăm că algoritmul se termină întotdeauna.
• Bucle exterioară rulează de n-1 ori (0 până la n-2), deoarece ultimul element va fi deja în
poziția corectă.
• La fiecare pas, dimensiunea părții nesortate scade cu 1, deci algoritmul ajunge inevitabil la o
secțiune goală și se oprește.
• Deoarece n-1 este un număr finit, Selection Sort se termină întotdeauna.
Concluzie
1. Am demonstrat prin inducție că după k iterații, primele k elemente sunt sortate și sunt cele
mai mici din vector.
2. După n-1 iterații, întregul vector este sortat.
3. Algoritmul se termină întotdeauna.
Pasul de inducție
Presupunem că pentru n = k algoritmul funcționează corect. Adică:
Suma teoretica Sk=k(k+1)/2
iar numărul lipsă este dat corect de formula:
lipsa=Sk−Sactual
Pentru n = k+1, noua sumă este:
Sk+1=(k+1)(k+2)/2
iar A conține k elemente. Aplicând algoritmul pe A, diferența dintre S_{k+1} și S_actual va
da exact numărul lipsă. Prin ipoteza de inducție, rezultatul este corect și pentru k+1.
4. Concluzie
Demonstrarea prin invariant:
Am arătat că diferența dintre suma teoretică și suma elementelor din A rămâne constant
egală cu numărul lipsă pe tot parcursul execuției.
Demonstrarea prin inducție:
Am verificat cazul de bază și am arătat că, dacă algoritmul funcționează pentru n = k, atunci
funcționează și pentru n = k+1.
Prin urmare, algoritmul este corect și rezolvă problema numărului lipsă pentru orice n.
I. Algoritmi probabilisti
Un algoritm probabilistic este un algoritm care folosește aleatoriu (randomness) în
procesul său de execuție. Spre deosebire de algoritmii deterministici, care produc întotdeauna
același rezultat pentru aceeași intrare, algoritmii probabilistici pot returna rezultate diferite pe
execuții diferite.
Există două mari categorii de algoritmi probabilistici:
1. Algoritmi Las Vegas (100% corecți, dar timp de rulare variabil)
• Acești algoritmi oferă întotdeauna un răspuns corect, dar timpul lor de execuție
poate varia în funcție de alegerile aleatorii făcute.
• Dacă au o limită de timp rezonabilă, sunt foarte utili.
2. Algoritmi Monte Carlo (timp fix, dar pot da răspuns greșit cu probabilitate mică)
• Acești algoritmi au un timp de execuție garantat, dar pot returna un rezultat
incorect cu o anumită probabilitate.
• Prin repetarea algoritmului, putem face probabilitatea de eroare foarte mică.
def coin_flip_sum(n):
sum_x = 0
for _ in range(n):
sum_x += [Link]([0, 1]) # Aruncăm moneda
return sum_x
def asteptare():
i=0
while [Link]([0, 1]) != 0: # Continuă până apare 0 (cap)
i += 1
return i + 1
Fie x₀, x₁, x₂, ..., xₙ₋₁ elementele mulțimii S, ordonate crescător.
Alegem x aleator la început. Dacă x = xᵢ, atunci vom compara x cu toate elementele care sunt mai
mici decât xᵢ, deci cu elementele x₀, x₁, ..., xᵢ₋₁.
Probabilitatea ca x să fie elementul xᵢ este: 1/n. Când x = xᵢ, numărul de atribuiri x = y este
exact i.
Numarul mediu de atribuiri este: E[nr atribuiri]=∑1/n*i=1/n∑i
Suma numerelor naturale: ∑i = n(n−1)/2
Rezultatul final: E[nr atribuiri]=1/n*n(n−1)/2=(n−1)/2
alg(S, M):
choose S' ⊆ S # Alege simultan toate subseturile
if sum(S') == M:
return "DA"
else:
return "NU"
Intuiție:
• În loc să verificăm fiecare subset pe rând, algoritmul nedeterminist "ghicește" direct
subsetul corect.
• Apoi, doar verifică dacă suma subsetului este M, ceea ce se face în timp polinomial.
1. ban maslui, cap = 5/9 si pajura 4/9
def arunca_ban_masluit():
std::uniform_int_distribution<> dist(0,8);
int x = dist(gen);
if x < 5:
return "cap"
else:
return "pajura"
2. un algoritm
ok = true;
i = 0;
while (ok) {
uniform x from {0, 1};//alege 0 sau 1 cu distribut, ie uniform ̆a
if (x == 0) {
ok = false;
}
++i;
}
print(i);
3. un algoritm
def algoritm_probabilist(n):
sum = 0
for _ in range(n):
x = [Link]([0, 1])
sum += x
return sum
def rand2():
# Funcția probabilistă de bază care returnează 0 sau 1 cu probabilitate 0.5
return (some_external_source() % 2) # Simulăm rand2 fără random
def zar():
while True:
# Generăm un număr pe 3 biți: x = b2b1b0
x = (rand2() << 2) | (rand2() << 1) | rand2()
if x < 6: # Acceptăm doar valorile 0-5
return x
Explicație
1. Generare pe 3 biți:
• rand2() << 2 → bitul cel mai semnificativ
• rand2() << 1 → bitul din mijloc
• rand2() → bitul cel mai puțin semnificativ
• Aceasta creează un număr între 0 și 7 (ex. 000 = 0, 111 = 7).
2. Refuzarea numerelor 6 și 7
• Dacă x este 6 sau 7, repetăm procesul pentru a menține probabilități egale.
• Acest lucru nu introduce bias, deoarece distribuția rămâne uniformă între 0 și 5.
def rand2p():
# Aceasta este funcția probabilistă dată, care returnează 0 cu probabilitate p și 1 cu 1 - p.
# Nu știm valoarea exactă a lui p.
pass # Trebuie înlocuită cu implementarea reală.
def rand2corect():
while True:
a = rand2p()
b = rand2p()
if a == 0 and b == 1:
return 0
elif a == 1 and b == 0:
return 1
# Dacă (a, b) este (0,0) sau (1,1), repetăm procesul
# Testare
for _ in range(10):
print(rand2corect(), end=" ")
Explicație și Justificare
• Neuniformitatea lui rand2p() este eliminată, deoarece alegem doar cazurile (0,1) și (1,0),
care apar cu aceeași probabilitate.
• Procesul este garantat să se termine, deoarece șansa de a repeta scade exponențial (în
medie durează 1/(2p(1-p)) pași).
• Nu folosim nicio altă sursă de aleatoriu, respectând cerința problemei.
6. INPUT: a0, a1, …, an-1; ai∈1..n; ai!=aj; oricare i,j∈0..n-1; i!=j, k∈N
OUTPUT: i∈N, 0<=i<=n-1; ai. ai=k
alg nedet(a,k) {
//ghicire
int i = rand(0...n-1)
if(a[i] == k) return i;
failure;
}
7.
SSD1 - Cel mai mare sub-sumă posibilă ≤ M
Strategie:
• Alegem nedeterminist un subset S' ⊆ S.
• Verificăm dacă suma elementelor lui S' este ≤ M.
• Găsim cel mai mare astfel de M*.
max = v[0]
sum = 0
for i = 0 to n-1:
if v[i] > max:
max = v[i]
for j = 0 to n-1:
sum = sum + 1 # Bucla interioară
return sum
2. Pentru acest algoritm probabilist, vrem să calculăm timpul mediu de execuție. Să analizăm pașii
algoritmului și cum putem determina numărul mediu de iterații pe care le va face bucla for, ținând
cont de faptul că vectorul v conține numere naturale, iar fiecare element are o probabilitate
uniformă de a fi par sau impar.
Seminar 5 – NP Completitudine
1. 2-COL
INPUT: un graf neorientat G=(V,E), V este mulțimea de noduri (vârfuri), cu ∣V∣=n, E⊆V×V
este mulțimea muchiilor, cu ∣E∣=m, O muchie (u,v)∈E înseamnă că există o
conexiune între nodurile u și v, fiecare nod vi∈V are un grad d(vi) dat de numărul de
noduri adiacente
OUTPUT: f(G)={1 daca G este 2-colorabil; 0 altfel}
DEMONSTRATIE:
Pentru a demonstra că 2-COL aparține clasei P, trebuie să arătăm că poate fi rezolvată în
timp polinomial.
Pasii algoritmului:
• Alegem un nod de start și îl colorăm cu culoarea 1.
• Parcurgem graful folosind BFS (sau DFS).
• Pentru fiecare nod vizitat:
• Dacă nu este colorat, îl colorăm cu culoarea opusă față de părinte.
• Dacă este deja colorat și are aceeași culoare ca părintele → graful NU este bipartit,
deci nu poate fi 2-colorabil.
• Repetăm procesul pentru toate componentele conexe.
• Dacă nu găsim conflicte, returnăm "Graful este 2-colorabil".
• Algoritmul BFS/DFS rulează în O(V + E) (timp polinomial).
• Deci, 2-COL ∈ P, deoarece se poate rezolva determinist în timp polinomial.
2. 2-SAT
INPUT: X={x1,x2,…,xn}, Φ=C1∧C2∧⋯∧Cm in CNF, Ci=(ℓi1∨ℓi2)
OUTPUT: Dacă există o soluție validă, returnăm "SATISFIABIL" și o afectare validă.
Dacă nu există nicio afectare validă, returnăm "NESATISFIABIL".
DEMONSTRATIE:
• Se construiește un graf de implicație G=(V,E), unde:
• Nodurile sunt literalii (xj și ¬xj).
• Muchiile reprezintă implicațiile logice.
Construim graful de implicație în timp O(n+m).
• Calculăm componentele tare conexe
• Dacă un literal și negarea lui apar în aceeași componentă tare conexă, formula nu este
satisfiabilă.
Complexitatea totală este O(n+m) (liniară).
Algoritmul este determinist și rulează în timp polinomial.
• Deoarece P conține toate problemele care pot fi rezolvate într-un timp polinomial, rezultă
că 2-SAT aparține clasei P
3. Sortarea unui vector
INPUT: V=(v1,v2,…,vn), vi∈ {1, …, n}
OUTPUT: V′=(v1′,v2′,…,vn′), vi′ ∈ {1, …, n}, v1′≤v2′≤⋯≤vn′
DEMONSTRATIE:
Pentru a demonstra că problema sortării unui vector aparține clasei P, trebuie să arătăm
că poate fi rezolvată de un algoritm determinist într-un timp polinomial față de dimensiunea
inputului. Există algoritmi deterministi cunoscuți care sortează un vector în timp polinomial:
• MergeSort – timp O(nlogn)
• QuickSort (în medie) – timp O(nlogn)
• HeapSort – timp O(nlogn)
• BubbleSort, InsertionSort, SelectionSort – timp O(n^2) (mai puțin eficienți, dar tot
polinomiali)
Sortarea este o problemă computațională care poate fi rezolvată eficient folosind algoritmi
deterministi în timp O(nlogn). Deoarece O(nlogn) este polinomial, rezultă că problema sortării
aparține clasei P.
DEMONSTRATIE:
Pentru a demonstra că problema căutării într-o listă sortată aparține clasei P, trebuie să
arătăm că poate fi rezolvată de un algoritm determinist într-un timp polinomial față de
dimensiunea inputului.
Există două metode deterministe principale pentru căutare:
(a) Căutare Liniară
• Parcurge lista secvențial și compară fiecare element cu x.
• Are complexitate O(n).
(b) Căutare Binară (Binary Search)
• Alege elementul din mijloc și compară cu x.
• Dacă x este mai mic, caută în prima jumătate, altfel caută în a doua jumătate.
• Se repetă până când se găsește x sau se termină lista.
• Are complexitate O(logn).
Căutarea într-o listă sortată se poate rezolva eficient cu căutare binară în O(logn), care este
polinomială. Prin urmare, problema aparține clasei P.
DEMONSTRATIE:
Algoritmul clasic pentru înmulțirea a două matrici presupune trei bucle for:
• for i=1 to m(randuri din A)
• for k=1 to n(coloane din B)
• for j=1 to p(elemente comune ın ınmulțire) cik=cik+aij*bjk
Complexitatea acestui algoritm este: O(m*p*n) care este polinomială în dimensiunile
matricelor.
Deoarece există un algoritm determinist care rulează în timp polinomial, rezultă că
problema înmulțirii a două matrici aparține clasei P.
6. SHORTEST-PATH
INPUT: un graf G=(V,E,w) și două noduri s,t∈V, V este mulțimea de noduri, E⊆V×V este
mulțimea de muchii, w:E→R+ este o funcție de ponderi asociate muchiilor, s,t∈V sunt nodurile
sursă și destinație
OUTPUT: un drum de cost min P=(v0,v1,...,vk) cu v0=s și vk=t, cost(P)∑w(vi,vi+1)
DEMONSTRATIE
Problema SHORTEST-PATH poate fi rezolvată într-un timp polinomial de un algoritm
determinist. De exemplu:
Algoritmul lui Dijkstra este un algoritm determinist care, pentru un graf cu V noduri și E
muchii, rulează în timp polinomial în funcție de V și E, adică în O((V+E)logV)
7. EULER-TOUR
INPUT: un graf neorientat G=(V,E),
OUTPUT: Fiecare muchie e∈E leagă două noduri u și v (adică e=(u,v)).
DEMONSTRATIE
Algoritmul lui Hierholzer este cel mai cunoscut algoritm pentru rezolvarea problemei
EULER-TOUR. Acesta funcționează într-un timp liniar în funcție de numărul de muchii și noduri,
adică în O(E), unde E este numărul de muchii din graf.
Algoritmul lui Hierholzer:
1. Verificarea condițiilor pentru turul eulerian:
• Verificăm dacă toate nodurile au gradul par și dacă graful este conectat. Dacă oricare
dintre aceste condiții nu este îndeplinită, nu există un tur eulerian.
2. Construirea turului:
• Dacă graful satisface condițiile pentru a avea un tur eulerian, putem construi turul
folosind algoritmul lui Hierholzer. Acesta selectează un drum arbitrar care nu a fost
încă parcurs complet, parcurge o muchie care nu a fost traversată și repetă acest
proces până când toate muchiile au fost vizitate.
3. Complexitatea:
• Algoritmul parcurge fiecare muchie o singură dată. Într-un graf cu E muchii, acest
proces va dura O(E) pași.
Clasa de probleme NP
Clasa NP (NPTIME) = {X | X este o problemă de decizie si există un algoritm nedeterminist
polinomial ı̂n cazul cel mai nefavorabil pentru X}
O problemă X este NP-dificilă dacă orice problemă Y∈NP se poate reduce polinomial la X,
adică: Y≤pX,∀Y∈NP.
O problemă X este NP-completă dacă:
1. Este în NP: Există un algoritm de verificare care poate verifica o soluție în timp polinomial.
X∈NP
2. Este NP-dificilă: Orice problemă Y∈NP se poate reduce polinomial la X, adică:
Y≤pX,∀Y∈NP
1. VERTEX-COVER
INPUT: un graf neorientat G = (V, E), k ∈ N
OUTPUT: există V’⊆ V a.ı̂. |V’| ≤ k si pentru orice muchie {u, v} ∈ E avem u ∈ V’ sau
v ∈ V’ ?
DEMONSTRATIE
Pentru a arăta că Vertex Cover este NP-completă, facem o reducere polinomială de la 3-
SAT (un problem NP-completă):
• Pasul 1: Transformarea formulei 3-SAT într-un graf
• Fiecare literă și negarea sa din formula 3-SAT devin noduri într-un graf.
• Fiecare clauză cu 3 literale devine un triunghi de 3 noduri conectate, fiecare nod
corespunzând unui literal sau complementul acestuia.
• Pasul 2: Aplicarea problemei Vertex Cover
• Dacă formula 3-SAT este satisfiabilă, există o alegere de literale adevărate care poate
fi reprezentată printr-un vertex cover de dimensiune m.
• Vertex cover-ul va acoperi muchiile între nodurile literale și clauze, acoperind astfel
întreaga formulă.
• Vertex Cover este în NP și poate fi redusă în mod polinomial din 3-SAT, ceea ce dovedește
că este NP-completă.
2. HAMILTONIAN-CIRCUIT
INPUT: un graf orientat G = (V, E);
OUTPUT: există un circuit hamiltonian ı̂n G? (o permutare v1 , v2 , . . . vn a nodurilor din V
astfel ı̂ncât vi , vi+1 ∈ E (pentru 1 ≤ i < n) si vn , v1 ∈ E))
DEMONSTRATIE
Pentru a demonstra că problema Hamiltonian Circuit este NP-completă, urmează acești
pași sintetizați:
1. Hamiltonian Circuit este în NP:
• Problema Hamiltonian Circuit se află în clasa NP deoarece, dat un circuit posibil, putem
verifica într-un timp polinomial dacă este un circuit valid (vizitează fiecare nod o singură
dată și se întoarce la nodul de început).
DEMONSTRATIE
Pentru a demonstra că problema Hamiltonian Tour este NP-completă, urmează pașii
sintetizați:
1. Hamiltonian Tour este în NP:
• O soluție posibilă este un ciclu Hamiltonian (vizitează fiecare nod exact o dată și se întoarce
la nodul de început).
• Verificarea validității acestui ciclu se face într-un timp polinomial (O(n)), deci Hamiltonian
Tour este în NP.
2. Reducerea din 3-SAT la Hamiltonian Tour:
• Fiecare problemă 3-SAT (NP-completă) poate fi transformată într-un graf în care un ciclu
Hamiltonian va reprezenta o soluție pentru formula 3-SAT.
• Reducerea implică construirea unui graf unde nodurile și muchiile reflectă literalele și
clauzele formulei 3-SAT, iar un ciclu Hamiltonian valid reprezintă satisfacerea formulei.
Hamiltonian Tour este NP-completă deoarece este în NP și există o reducere polinomială
de la problema NP-completă 3-SAT.
4. MAXIMUM-INDEPENDENT-SET
INPUT: un graf neorientat G = (V, E), k ∈ N
OUTPUT: există V’ ⊆ V a.ı̂. |V’| ≥ k si pentru orice u, v ∈ V’ , avem {u, v} nu ∈ E?
DEMONSTRATIE
Pentru a demonstra că problema Maximum Independent Set este NP-completă, urmează
pașii sintetizați:
1. Problema Maximum Independent Set este în NP:
• O soluție posibilă este un subset de noduri care constituie un set independent.
• Verificarea dacă acest subset este independent și dacă dimensiunea acestuia este ≥k se face
într-un timp polinomial (O(∣E∣)).
• Prin urmare, problema este în NP.
2. Reducerea din Vertex Cover la Maximum Independent Set:
• Problema Vertex Cover (NP-completă) poate fi redusă la Maximum Independent Set.
• Un vertex cover de dimensiune k într-un graf are complementul său, un set
independent de dimensiune ∣V∣−k.
• Astfel, un vertex cover de dimensiune k echivalează cu un set independent de
dimensiune cel puțin ∣V∣−k.
• Reducerea este realizată într-un timp polinomial.
5. CLIQUE
INPUT: un graf neorientat G = (V, E), k ∈ N
OUTPUT: există V⊆ V a.ı̂. |V| ≥ k si pentru orice u ∈ V’, v ∈ V’, u != v avem muchia
{u, v} ∈ E?
DEMONSTRATIE
Pentru a demonstra că problema Clique este NP-completă, urmează pașii sintetizați:
1. Problema Clique este în NP:
• O soluție posibilă pentru problema Clique este un subset de k noduri.
• Verificarea dacă acest subset formează un clique (adică dacă toate perechile de noduri sunt
conectate prin muchii) se face într-un timp polinomial (O(k2)).
• Prin urmare, problema este în NP.
2. Reducerea din Vertex Cover la Clique:
• Problema Vertex Cover (NP-completă) poate fi transformată într-o instanță a problemei
Clique în graful complementar.
• Un vertex cover de dimensiune k într-un graf G echivalează cu un clique de
dimensiune ∣V∣−k în graful complementar G′.
• Reducerea se face într-un timp polinomial.
Clique este în NP și există o reducere polinomială de la Vertex Cover la aceasta.
6. PARTITION
INPUT: S={x1,x2,…,xn}
OUTPUT: Există două subseturi S1⊆S și S2⊆S astfel încât:
• S1∩S2=∅ (adică sunt disjuncte),
• S1∪S2=S (adică uniunea lor este S),
• ∑xi∈S1xi=∑xj∈S2xj.
DEMONSTRATIE
Pentru a demonstra că problema Partition este NP-completă, realizăm următoarele pași
sintetizați:
1. Partition este în NP:
• O soluție posibilă pentru problema Partition este un subset S1⊆S.
• Verificarea dacă suma elementelor din S1 este egală cu suma elementelor din S2
=S∖S1 se face într-un timp polinomial, deoarece presupune doar calcularea sumelor
celor două subseturi.
• Astfel, Partition este în NP.
2. Reducere din Subset-Sum la Partition:
• Subset-Sum este o problemă NP-completă.
• Dăm ca input pentru problema Partition un set S′={x1,x2,…,xn,T}, adăugând un
număr T la setul S.
• Dacă putem împărți setul S′ în două subseturi cu suma egală, atunci suma unui subset
va fi exact T, ceea ce echivalează cu problema Subset-Sum.
3. Concluzie:
• Deoarece Partition este în NP și există o reducere polinomială din Subset-Sum
(NP-completă) la Partition, rezultă că Partition este NP-completă.
7. SUBSET-SUM
INPUT: S ⊆ N, t ∈ N
OUTPUT: există S ⊆ S astfel ı̂ncât Σx∈S’ x = t?
DEMONSTRATIE
Pentru a demonstra că problema Subset Sum este NP-completă, urmează pașii sintetizați:
1. Problema Subset Sum este în NP:
• O soluție posibilă este un subset S′ din S.
• Verificarea dacă suma elementelor din subsetul S′ este egală cu T se face într-un timp
polinomial, O(n).
• Astfel, problema este în NP.
2. Reducerea din 3-SAT la Subset Sum:
• 3-SAT este NP-completă, iar reducerea se face într-un timp polinomial.
• Fiecare literă din formula 3-SAT este reprezentată de un număr.
• Clauzele sunt reprezentate de combinații de numere.
• Un subset de numere din 3-SAT poate fi ales astfel încât suma să fie T dacă și numai
dacă formula 3-SAT este satisfăcută.
Subset Sum este în NP și există o reducere polinomială de la 3-SAT la [Link] urmare,
Subset Sum este NP-completă.
8. 3-SAT
INPUT: Φ=C1∧C2∧⋯∧Cm, Φ în CNF, unde fiecare Ci este o clauză și are forma:
Ci=(li1∨li2∨li3)
OUTPUT: True (sau 1) dacă există o asignare de valori booleene pentru variabilele
x1, x2, …, xn astfel încât formula Φ să fie satisfăcută (adică fiecare clauză să fie adevărată).
False (sau 0) dacă nu există nicio asignare de valori booleene pentru variabile care să
satisfacă formula Φ.
DEMONSTRATIE
Demonstratia ca problema 3-SAT este NP-completă:
1. 3-SAT este în NP:
• O soluție pentru problema 3-SAT este o asignare de valori pentru variabilele
booleene.
• Verificarea dacă formula booleeană este satisfăcută pentru această asignare se face
într-un timp polinomial (evaluând fiecare clauză). Astfel, 3-SAT este o problemă în
NP.
2. Reducerea din SAT la 3-SAT:
• SAT este o problemă NP-completă.
• Orice formulă CNF (Conjunctivă Normal Form) din SAT poate fi transformată într-o
formulă 3-SAT.
• Dacă o clauză are mai mult de 3 literale, o descompunem într-o combinație de clauze
cu 3 literale folosind literale suplimentare.
• Dacă o clauză are mai puțin de 3 literale, o completăm cu literale suplimentare până
ajungem la 3.
3. Concluzie:
• Deoarece 3-SAT este în NP și orice problemă NP-completă (SAT) poate fi redusă la
3-SAT într-un timp polinomial, 3-SAT este NP-completă.
9. 3-COL
INPUT: G = (V, E)
OUTPUT: este G 3-colorabil?
DEMONSTRATIE
Pentru a demonstra că problema 3-Coloring este NP-completă, pașii sintetizați sunt
următorii:
1. Problema 3-COL este în NP:
• O soluție posibilă este o colorare a nodurilor grafului cu cel mult 3 culori.
• Verificarea dacă această colorare este validă (adică nodurile adiacente nu au aceeași culoare)
se face într-un timp polinomial.
• Prin urmare, 3-COL este în NP.
2. Reducerea din 3-SAT la 3-Coloring:
• 3-SAT (o problemă NP-completă) poate fi transformată într-un graf.
• Fiecare literă și clauză din formula 3-SAT este reprezentată de noduri și muchii în graf.
• Construcția grafului este făcută astfel încât să existe o colorare validă cu 3 culori dacă și
numai dacă formula 3-SAT este satisfăcută.
• Reducerea se face într-un timp polinomial.
Concluzie:
• 3-Coloring este în NP și există o reducere polinomială de la 3-SAT la aceasta.
• Prin urmare, 3-Coloring este NP-completă.
10. CIRCUIT-SAT
INPUT: un circuit combinat, ional C
OUTPUT: există date de intrare astfel ı̂ncât circuitul să producă output-ul 1?
DEMONSTRATIE
Pentru a demonstra că problema Circuit-SAT este NP-completă, pașii sintetizați sunt
următorii:
1. Problema Circuit-SAT este în NP:
• O soluție posibilă este o asignare de valori booleene pentru intrările circuitului.
• Verificarea dacă această asignare satisface ieșirile dorite se face într-un timp polinomial,
deoarece evaluarea unui circuit logic este un proces polinomial.
• Prin urmare, Circuit-SAT este în NP.
2. Reducerea din 3-SAT la Circuit-SAT:
• 3-SAT este o problemă NP-completă.
• Fiecare instanță a 3-SAT este transformată într-un circuit logic, unde fiecare literă este
reprezentată de un bit și fiecare clauză de o combinație de porți logice.
• Un circuit va evalua formula 3-SAT și va fi satisfăcut dacă și numai dacă formula este
satisfăcută.
Concluzie:
• Circuit-SAT este în NP.
• Există o reducere polinomială din 3-SAT (NP-completă) la Circuit-SAT.
• Prin urmare, Circuit-SAT este NP-completă.
11. LONGEST-PATH
INPUT: Graf G=(V,E), vârf de început s=v1
OUTPUT: lungimea celui mai lung drum: 3 (drumul fiind v1→v3→v4, sau v1→v2→v3
→v4).
DEMONSTRATIE:
Sintetizarea demonstrației că problema Longest-Path este NP-completă:
1. Longest-Path este în NP:
• Problema Longest-Path este o problemă de decizie, unde întrebarea este dacă există
un drum de lungime cel puțin k într-un graf. Se poate verifica într-un timp
polinomial, deoarece, având drumul, putem contoriza numărul de muchii pentru a
verifica lungimea.
2. Reducerea din Hamiltonian-Path la Longest-Path:
• Problema Hamiltonian-Path (care este NP-completă) este redusă la Longest-Path.
Dacă există un drum Hamiltonian într-un graf G, atunci există un drum de lungime
∣V∣−1 (unde ∣V∣ este numărul de vârfuri din graf). Astfel, întrebarea dacă există un
drum Hamiltonian este echivalentă cu întrebarea dacă există un drum de lungime
∣V∣−1 în problema Longest-Path.
3. Concluzie:
• Deoarece Longest-Path este în NP și Hamiltonian-Path poate fi redusă la Longest-
Path într-un timp polinomial, Longest-Path este NP-completă.
I. Algoritmul KMP
Ideea principală este de a refolosi informatia obtinută prin comparatiile de la un anumit
deplasament pentru a reduce din comparatiile necesare la deplasamentele ulterioare.
O frontieră a unui șir este cel mai lung prefix propriu (adică nu tot șirul) care este și sufix
al acelui șir.
După o nepotrivire în KMP, nu reluăm căutarea de la începutul pattern-ului. În schimb,
ne uităm la frontiera șirului potrivit până atunci și repoziționăm pattern-ul astfel încât această
frontieră să se alinieze cu sufixul din text.
Componentele principale
1. Construirea tabelului "lps" (longest prefix suffix):
• Pentru fiecare poziție din pattern, determină cea mai lungă secvență proprie care e și
prefix și sufix.
2. Parcurgerea textului cu ajutorul tabelului lps, evitând comparații repetate.
Exemplu practic:
Pentru pattern-ul: "ABABAC" parcurgem fiecare poziție și construim:
i pattern[i] lps[i] Explicație
0 A 0 Nu are frontieră
1 B 0 A≠B
2 A 1 A = A (prefix A)
3 B 2 AB = AB
4 A 3 ABA = ABA
5 C 0 nu se potrivește, niciun sufix/prefix
Exemplu:
TEXT = HIABABXABABXABABY (lungime n = 17)
pattern = ABABXABABY (lungime m = 10)
Nepotrivire la k = 9
Folosim LPS:
• lps[9] = 0
➡️k devine 0 (niciun sufix util), dar i rămâne 2
➡️pattern-ul sare direct la poziția i = i + k - lps[k] = 2 + 9 - 0 = 11
🔄 Reluăm de la i = 11, k = 0
T[11] = X, P[0] = A → ❌
➡️i = 12
T[12] = A, P[0] = A → ✔️
➡️k = 1
T[13] = B, P[1] = B → ✔️
➡️k = 2
T[14] = A, P[2] = A → ✔️
➡️k = 3
T[15] = B, P[3] = B → ✔️
➡️k = 4
T[16] = Y, P[4] = X → ❌
➡️backtrack folosind lps[4] = 0, deci k = 0
➡️i = 17 > n - m → terminat
Exercitiul 1: In timpul executiei algoritmului KMP, se verifica daca pattern-ul ababa apare ın
textul bbabaababa la pozitia i = 2. Primele k = 3 caractere se potrivesc, iar al 4-lea caracter nu se
potriveste. Care este urmatorea pozitie a pattern-ului, dupa procesarea nepotrivirii? Care este
numarul de caractere care se potrivesc sigur la noul deplasament?
Date:
• Pattern: ababa
• Text: bbabaababa
• Poziția curentă în text: i = 2
• Primele k = 3 caractere se potrivesc, dar al patrulea nu.
Pas 1: Calculăm prefix function (pi[]) pentru pattern-ul ababa
Prefix function π[i] = lungimea celui mai lung prefix propriu al P[0..i] care este și sufix.
P[i] = [‘a’, ‘b’, ‘a’, ‘b’, ‘a’] π[i] = [0 0 1 2 3]
Important:
• cautarea se face de la dreapta la stanga;
• indentificam caracterul rau (BC) si sufixul bun (GS);
• BC → caracterul din text care nu s-a potrivit;
• GS → overlap-ul (potrivirea)
Componente cheie
1. Regula caracterului rău (Bad Character Rule)
• Dacă apare o nepotrivire, te uiți la caracterul din text care a provocat-o.
• Muti patternul astfel încât ultima apariție a acelui caracter în pattern să se alinieze cu
caracterul din text. Dacă nu apare în pattern, poți muta patternul complet după acel caracter.
2. Regula sufixului bun (Good Suffix Rule)
• Dacă ai o potrivire parțială la sfârșitul patternului, dar apare o nepotrivire, încerci să aliniezi
un altă apariție a acestui sufix în pattern sau un prefix potrivit.
• Astfel eviți să reîncepi căutarea de la zero.
Pașii algoritmului
1. Preprocesare:
• Construiești două tabele:
• Tabelul de caracter rău – O hartă cu ultima poziție a fiecărui caracter din
pattern.
• Tabelul de sufix bun – Spune cât de mult poți să muți patternul dacă un sufix
s-a potrivit.
2. Căutare:
• Aliniezi patternul cu textul.
• Compari caracterele de la dreapta la stânga.
• La nepotrivire, alegi între cele două reguli (cea care dă deplasarea mai mare).
• Repeți până când patternul nu mai încape în text.
Exemplu
Text: "ABAAABCD"
Pattern: "ABC"
Căutarea
Pasul 1: Aliniere inițială
Aliniem patternul sub text:
T: A B A A A B C D
ABC
Comparam de la dreapta la stânga:
• C (pattern) vs A (text, poziția 2) → ❌ nepotrivire
• Caracterul rău este A
• În pattern, A este la poziția 0
• Nepotrivirea a fost la poziția 2 (index în pattern)
• Calculăm deplasarea: max(1, 2 - last(A)) = max(1, 2 - 0) = 2
Exercitiul 1:
1. Pattern: oxoxoyoxo
• BC[i] = [‘x’: 6, ‘o’: 7, ‘y’: 4]
• GS[i] = [9, 9, 9, 9, 9, 6, 6, 3, 1]
2. Pattern: ABCWABCYABXAB
• BC[i] = [‘A’: 11, ‘B’: 12, ‘C’: 6, ‘W’: 3, ‘X’: 10, ‘Y’: 7]
• GS[i] = [13, 13, 13, 13, 13, 13, 13, 13, 10, 7, 4, 1, 1]
◦ h[i] = [2 1 0 0 2 1 0 0 2 1 0 0 0]
◦ f[i] = [0 0 0 0 1 2 3 0 1 2 0 1]
◦ R[i] = []
◦ g[i] = []
Important:
• asociaza un numar pattern-ului si subsirurilor de lungime m din text;
• E – alfabet; |E| = k => fiecare litera un are asociata un nr de la 0 la k-1;
• P = A B C D , A-0, B-1, C-2, p-prim; p=23;
◦ 0 1 23
• h(P) = ( 0*26^3 + 1*26^2 + 2*26^1 + 0*26^0 ) %p
• pot aparea subsiruri diferite cu valori hash egale => verificare;
Cum funcționează?
Presupunem că:
• P este pattern-ul, de lungime m
• T este textul, de lungime n
• H(x) este funcția hash pentru un șir x
Pași:
1. Calculăm hash-ul pattern-ului: H(P)
2. Calculăm hash-ul primului subșir de lungime m din text: H(T[0..m-1])
3. Pentru fiecare poziție i de la 0 la n - m:
• Dacă H(P) == H(T[i..i+m-1]), atunci comparăm direct șirurile caracter cu caracter
• Dacă se potrivesc, am găsit o apariție
• Dacă nu, continuăm
4. Pentru eficiență, folosim rolling hash: hash-ul pentru T[i+1..i+m] se calculează rapid din
T[i..i+m-1], fără recalcul complet.
La fiecare pas:
H(T[i+1..i+m]) = (b * (H(T[i..i+m-1]) - T[i]*b^(m-1)) + T[i+m]) mod q
Exemplu
• Pattern: abc
• Text: ababcabcabc
• Baza hash-ului: b = 256 (pentru caractere ASCII)
• Modulul: q = 101 (un număr prim pentru a evita coliziunile)
1.2 Calculăm hash-ul pentru primul subșir din text T = "ababcabcabc", adică subșirul T[0..2] =
"aba"
Aplicăm aceeași formulă:
H("aba") = (97 * 256^2 + 98 * 256^1 + 97 * 256^0) mod 101
H("aba") = (97 * 65536 + 98 * 256 + 97 * 1) mod 101
H("aba") = (6360576 + 25088 + 97) mod 101
H("aba") = 6386761 mod 101
H("aba") = 53
Hash-ul pentru subșirul T[1..3] = "bab" este 54, care este egal cu hash-ul pattern-ului. Așadar,
există o potrivire potențială la poziția 1.
3.2 Comparăm caracter cu caracter
Am obținut că hash-urile sunt egale, dar trebuie să facem și o comparație directă a caracterelor:
• Subșirul "bab" se potrivește cu pattern-ul "abc"?
• Comparăm fiecare caracter: T[1] = "b" vs P[0] = "a" → nu se potrivesc.
Deci, nu avem o potrivire la poziția 1.
Pasul 4: Continuăm căutarea pentru următoarele subșiruri
Acum, continuăm să calculăm hash-urile pentru următoarele subșiruri din text folosind același
rolling hash.
Exercitiul 1: Calculati hash-urile pattern-ului p, precum si a tuturor subsirurilor de lungime m ale lui
s pentru urmatoarele valori: p = aba, s = aabbababbabab, q = 3.
1. Formula de hash: hash(s)=(s0⋅d^m−1+s1⋅d^m−2+…+sm−1)modq
2. Hash-ul pattern-ului p = aba: hash(p) = (0⋅26^2+1⋅26^1+0⋅26^0)mod3 =(0+26+0)mod3 =
26mod3 = 2
3. Hash-urile subsirurilor de lungime m=3
Număr total de subșiruri: len(s) - m + 1 = 13 - 3 + 1 = 11
Vom calcula hash pentru fiecare subșir.
Poziție Subșir Valori (a=0, b=1) Hash formula Hash mod 3
0 aab 001 0×26² + 0×26 + 1 = 1 1
Poziție Subșir Valori (a=0, b=1) Hash formula Hash mod 3
1 abb 011 0×26² + 1×26 + 1 = 27 0
2 bba 110 1×26² + 1×26 + 0 = 702 + 26 = 728 728 % 3 = 1
3 bab 101 1×26² + 0×26 + 1 = 676 + 1 = 677 2
4 aba 010 0×26² + 1×26 + 0 = 26 2 ✅ (match)
5 bab 101 676 + 0 + 1 = 677 2
6 abb 011 26 + 1 = 27 0
7 bba 110 702 + 26 = 728 1
8 bab 101 676 + 1 = 677 2
9 aba 010 26 2 ✅ (match)
10 bab 101 676 + 1 = 677 2
Seminarul 7 – Backtracking
. R . . → linia 1, coloana 2
. . . R → linia 2, coloana 4
R . . . → linia 3, coloana 1
. . R . → linia 4, coloana 3
Validare matematică:
• Coloanele sunt diferite: 2, 4, 1, 3 → OK
• Diagonalele: nu există perechi i,j cu ∣qi−qj∣=∣i−j∣ → OK
Soluția 2: [3, 1, 4, 2]
Aceasta înseamnă:
• linia 1 → coloana 3
• linia 2 → coloana 1
• linia 3 → coloana 4
• linia 4 → coloana 2
. . R . → linia 1, coloana 3
R . . . → linia 2, coloana 1
. . . R → linia 3, coloana 4
. R . . → linia 4, coloana 2
Validare matematică:
• Coloanele: 3, 1, 4, 2 → OK
• Diagonalele: toate distanțele diagonale sunt unice → OK
Ideea principala: impartim problema data in mai multe subprobleme care se pot suprapune,
si combinam rezultatul acestora pentru a obtine rezultatul final. Tehnica de baza este tehnica de
memoizare care presupune retinerea rezultatelor subproblemelor intr-un array.
Exercitiul 1
INPUT: n∈N, sigma={X, O, Y}
OUTPUT: m∈N, ai nr = |{ S∈sigma* ai. [Link]=n si nu exsita 2 caractere consecutive
egale si fiecare O este incadrat de un X si un Y }|
n = 1 => {X}, {Y}
n = 2 => {XY}, {YX}
n = 3 => {XYX}, {YXY}, {XOX}, {YOY}
n = 4 => {XYXY}, {YXYX}, {XOYX}, {YOXY}, {XYOX}, {YXOY}
nr[n] = { 2, n=1, n=2 } || nr[n] = { nr[n-1] + nr[n-2], n>2 }
Exercitiul 2
Caz de baza: n=1
fib(2) fib(1) = 1 1 => 1 1=1 1
fib(1) fib(0) = 1 0 => 1 1=1 0
=> fib(1) = 1, fib(0)=0, fib(2)=1
Presupunem ca P(k):
fib(k+1) fib(k) = 1 1 la k si este adevarat
fib(k) fib(k-1) = 1 0
Demonstram P(k+1)
Exercitiul 3
combinari de n luate cate k = combinari de n-1 luate cate k-1 + combinari de n-1 luate cate k
Fie matrice c cu: - c[n][k] = 1, n=1, k=1 sau n=1, k=0
- c[n][k] = c[n-1][k-1] + c[n-1][k] % p, n>=2, k>=1
Exerciitul 4
S = 100101
100--1
--0--1
Exercitiul 5
n=1: 0, 1, 2,
n=2: 01, 02, 00, 10, 12, 11, 20, 21, 21
n=3: 101, 102, 110, 111, 112, 120, 121, 122, 201, 202, 210, 211, 212, 220, 221, 222
Exercitiul 6
Un șir bitonic este o secvență care crește și apoi descrește sau descrește și apoi crește (cu
un singur punct de inflexiune). Dat un șir S=[s1,s2,…,sn], vrem să găsim cea mai lungă
subsecvență (nu neapărat continuă) care este bitonică.
1. LIS↑(i): lungimea celei mai lungi subsecvențe crescătoare care se termină la i;
2. LIS↓(i): lungimea celei mai lungi subsecvențe descrescătoare care începe de la i;
Cazuri de bază:
Fiecare element singur e o secvență crescătoare sau descrescătoare: LIS↑(i)=1, LIS↓(i)=1
pentru orice i.
Relatii de recurenta
1. Relație pentru subsecvențe crescătoare: și LIS↑(i)=1+max{LIS↑(j)∣j<i și S[j]<S[i]}
Dacă nu există asemenea j, atunci LIS↑(i)=1
2. Relație pentru subsecvențe descrescătoare: și LIS↓(i)=1+max{LIS↓(j)∣j>i și S[j]<S[i]}
Dacă nu există asemenea j, atunci LIS↓(i)=1
Exercitiul 8
Ni se dă un triunghi de numere cu n niveluri. Trebuie să alegem o cale de la vârf la bază,
unde:
• la fiecare pas putem merge la stânga-jos sau dreapta-jos
• vrem să maximizăm suma numerelor alese
Vom nota:
DP(i,j)=suma maxima care se poate obține pana la poziția (i,j) unde:
• i este nivelul în triunghi (de sus în jos)
• j este poziția pe linia i (de la stânga la dreapta)
Relația de recurență
Pentru orice poziție (i,j), putem ajunge acolo din:
• poziția (i−1,j−1) — de sus-stânga
• poziția (i−1,j) — de sus-dreapta
Dar: poziția j trebuie să fie validă în acele cazuri.
DP(i,j)=T(i,j)+max(DP(i−1,j−1),DP(i−1,j))
Cu grijă la margini:
• Dacă j=1, nu există DP(i−1,j−1) → luăm doar DP(i−1,j)
• Dacă j=i, nu există DP(i−1,j) → luăm doar DP(i−1,j−1)
Relatia de recurenta
Putem calcula Si+1 din Si în timp constant, folosind diferență de margini:
Si+1=Si−v[i]+v[i+K]
Cazul de baza
C(0)=1 → Șirul vid (fără nicio paranteză) este considerat corect.
Relatia de recurenta
Pentru n≥1, putem construi un șir corect de forma:
(parantezare corecta cu k perechi) parantezare corecta cu n−1−k perechi
Pentru fiecare k∈[0,n−1], punem o paranteză deschisă, apoi o sub-parantezare cu k perechi,
închisă de ), urmată de o parantezare cu n−1−k perechi.
C(n)= k=0∑n−1 C(k)⋅C(n−1−k)
Exemplu:
Să calculăm primele valori:
• C(0)=1
• C(1)=C(0)⋅C(0)=1
• C(2)=C(0)⋅C(1)+C(1)⋅C(0)=1+1=2
• C(3)=C(0)C(2)+C(1)C(1)+C(2)C(0)=2+1+2=5
• C(4)=C(0)C(3)+C(1)C(2)+C(2)C(1)+C(3)C(0)=5+2+2+5=14
Deci, de exemplu: Pentru N=6 (adică 3 perechi): C(3) = 5 șiruri corect parantezate.
Șiruri valide pentru n=3:(
(()))
(()())
(())()
()(())
()()()
3. Cel mai lung drum de lungime impara carea leaga doua frunte intr-un arbore.
Fie T=(V,E) arbore, root = un nod arbitrar de pornire.
Definim pentru fiecare nod u:
• dpeven(u): lungimea maximă a unui drum par de la u până la o frunză din subarborele său
• dpodd(u): lungimea maximă a unui drum impar de la u până la o frunză din subarborele său
Caz de baza
Dacă u este frunză, atunci:
• dpeven(u)=0 (drumul către sine însuși are 0 muchii – par)
• dpodd(u)=−∞ (nu există drum impar de la frunză către alt nod în jos)
Relatia de recurenta
Pentru orice nod intern u, parcurgem toți copiii săi c și actualizăm:
dpeven(u)=max(dpeven(u),1+dpodd(c))
dpodd(u)=max(dpodd(u),1+dpeven(c))