#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <queue>
#include <unordered_set>
#include <utility>
#include <algorithm>
#include <string>
using namespace std;
const int nul = -1;
const int maxim = 1000000;
class Graf
{
int nr_noduri;
int nr_muchii;
vector<vector<int>> lista_adiacenta;
vector<vector<pair<int, int>>> muchii_cu_costuri;
void DFS(int nod, vector<bool>& vizitat);
//functie apelata din componente_conexe care face algoritmul de DFS pe
//un graf dat
void gasire_componenta_biconexa(int nod1, int nod2, vector<vector<int>>& comp_biconexe, stack<pair<int, int>>& muchii_comp_biconexe);
//functie apelata din componente_biconexe care creeaza un vector de muchii
//(componenta biconexa neprelucrata) si il pune in vectorul de
//componente biconexe
void cautare_componente_biconexe(int nod_curent, int nod_initial, int nv, vector<int>& nivel, vector<int>& low, vector<vector<int>>& comp_biconexe, stack<pair<int, int>>& muchii_comp_biconexe);
//functie care cauta componentele biconexe dintr-un graf
void DFS_tare_conexe(int nod, int& index_ctc, vector<int>& index, vector<int>& low, vector<bool>& inComponenta, stack<int>& noduri_comp_tare_conexa, vector<vector<int>>& comp_tare_conexe);
//functie apelata din componente_tare_conexe care cauta componente tare
//conexe si le pune intr-un vector
void DFS_muchii_critice(int nod, vector<int>& nivel, vector<int>& low, vector<vector<int>>& lista_muchii);
//functie apelata din muchii_critice care cauta muchii critice si le pune
//intr-un vector de muchii
int reprez_Kruskal(int nod, vector<int>& tata);
//functie apelata din apm_Kruskal care gaseste reprezentantul unui nod dat
void reunire_Kruskal(int nod1, int nod2, vector<int>& tata, vector<int>& inaltime);
//functie apelata din apm_Kruskal care face reuniunea componentelor a doua
//noduri date
int reprez_paduri(int nod, vector<int>& tata);
//functie apelata din paduri_disjuncte care gaseste reprezentantul unui
//nod dat
void reunire_multimi_paduri(int nod1, int nod2, vector<int>& tata, vector<int>& inaltime);
//functie apelata din paduri_disjuncte care face reuniunea componentelor
//a doua noduri date
bool BFS_construire_lant(vector<int>& tata, vector<vector<pair<int, int>>>& fluxuri, vector<unordered_set<int>>& lista_arce);
//functie apelata din edmonds_karp care construieste un lant pe unde
//putem sa adaugam flux
int revizuire_flux(vector<int>& tata, vector<vector<pair<int, int>>>& fluxuri);
//functie apelata din edmonds_karp care modifica fluxul pe lantul obtinut cu
//ajutorul functiei BFS_construire_lant
vector<int> gasire_ciclu_euler();
//functie apelata din ciclu_euler care gaseste si returneaza ciclul
//eulerian al unui graf care indeplineste conditia sa aiba acest ciclu
bool BFS_cuplaj_maxim(vector<int>& pereche_L, vector<int>& pereche_R, vector<int>& nivel);
//functie apelata din cuplaj_maxim_hopcroft_karp care face un BFS pe
//graful bipartit cu rolul de a gasi noduri din multimea R necuplate cu
//noduri din multimea L care au muchie spre ele
bool DFS_cuplaj_maxim(int nod_L, vector<int>& pereche_L, vector<int>& pereche_R, vector<int>& nivel);
//functie apelata din cuplaj_maxim_hopcroft_karp care face un DFS pe
//graful bipartit cu rolul de a modifica sau adauga un cuplaj pentru un
//nod dat din multimea L
public:
Graf();
void citire_neorientat(const string fis);
void citire_orientat(const string fis);
int citire_orientat_bfs(const string fis);
//functie de citire specifica problemei BFS
//pentru a permite citirea nodului de start
vector<vector<int>> citire_neorientat_cost(const string fis);
void citire_orientat_cost(const string fis);
vector<unordered_set<int>> citire_retea(const string fis);
//functie de citire specifica problemei
//Edmonds-Karp (maxflow); identica cu
//citire_orientat_cost, cu exceptia ca vom
//pune arcele si intr-un vector de hash-uri
//pentru a putea gasi arcele inverse in
//timpul algoritmului Edmonds-Karp
vector<vector<int>> citire_orientat_cost_matrice(const string fis);
//functie de citire specifica problemei
//Floyd-Warshall (royfloyd); citeste matricea
//de distante data
vector<vector<int>> citire_orientat_cost_hamilton(const string fis);
//functie de citire specifica algoritmului de
//gasire a ciclului hamiltonian; asemanator cu
//citire_orientat_cost, dar retinem costurile
//muchiilor separat si lista adiacenta
//"invers" pentru a cauta nodurile care au
//muchie spre un nod dat in algoritmul
//principal
void citire_arbore(const string fis);
//functie de citire specifica problemei
//diametrului arborelui (darb); identica cu
//citire_neorientat, cu exceptia faptului ca
//vom citi doar numarul de noduri (numarul
//de muchii este implicit nr_noduri - 1)
int citire_bipartit(const string fis);
//functie de citire specifica problemei
//Hopcroft-Karp (cuplaj maxim); identica cu
//citire_orientat, cu exceptia ca vom citi si
//numarul de noduri al multimii R
vector<int> BFS(int nod_start);
//functie care face algoritmul de BFS pe un graf pornind de la un nod de
//start dat
int componente_conexe();
//functie care foloseste DFS pentru a gasi cate componente conexe sunt
//intr-un graf
vector<vector<int>> componente_biconexe();
//functie care face initializarile pentru algoritmul de componente
//biconexe
vector<vector<int>> componente_tare_conexe();
//functie care incepe un algoritm de cautare a componentelor tare conexe
//si care returneaza un vector cu aceste componente
void havel_hakimi(vector<int>& secv_grade, int n, const string fis);
//functie care ia ca argument o secventa de grade si spune daca se poate
//construi un graf cu aceste grade folosind algoritmul Havel-Hakimi
vector<int> sort_top();
//functie care returneaza sortarea topologica a unui graf orientat
vector<vector<int>> muchii_critice();
//functie csre incepe un algoritm de cautare a muchiilor critice si care
//returneaza vectorul cu aceste muchii
vector<vector<int>> apm_Kruskal(vector<vector<int>>& muchii_ponderate, int& cost_apm);
//functie care ia un vector de muchii cu costuri ale unui graf. Ea va
//returna APM pentru graful dat si va modifica variabila cost_apm astfel
//incat sa stocheze costul APM-ului cerut
void paduri_disjuncte(const string fis_in, const string fis_out);
//functie care efectueaza operatii de reuniune de multimi si de
//aflare a egalitatii reprezentantilor a doua noduri date
vector<int> dijkstra();
//functie care ia un vector de muchii cu costuri pentru fiecare nod
//(pozitive). Ea returneaza un vector care reprezinta distantele de la
//nodul 1 la celelalte noduri
vector<int> bellman_ford(bool& exista_ciclu, const string fis);
//functie care ia un vector de muchii cu costuri pentru fiecare nod. Ea
//returneaza un vector care reprezinta distantele de la nodul 1 la
//celelalte noduri. Functia testeaza si daca exista un ciclu negativ in
//graf.
int edmonds_karp(vector<unordered_set<int>>& lista_arce);
//functie care ia un vector de muchii cu costuri pentru fiecare nod si un
//vector de hash-uri care retine lista de muchii pentru fiecare nod. Ea
//returneaza fluxul maxim care se poate trimite pe reteaua data.
vector<vector<int>> floyd_warshall(vector<vector<int>>& muchii_ponderate);
//functie care ia o matrice de distante pentru fiecare muchie. Ea
//returneaza o matrice de distante minime intre fiecare nod al grafului.
void BFS_diametru_arbore(int nod_start, int& ultim, int& diametru);
//functie care face algoritmul de BFS pe un graf pornind de la nodul de
//start, retinand la sfarsit ultimul nod parcurs si nivelul la care se
//afla acesta (care va fi diametrul arborelui dupa a doua apelare)
void ciclu_euler(vector<int>& ciclu_eulerian, const string fis);
//functie care verifica pentru un multigraf daca poate avea un ciclu
//eulerian si, daca da, apeleaza o functie care il gaseste
int ciclu_hamilton(vector<vector<int>>& costuri_muchii);
//functie care ia un graf orientat cu costuri si care verifica daca acesta
//are cel putin un ciclu hamiltonian; daca da, returneaza costul minim al
//unui astfel de ciclu
int cuplaj_maxim_hopcroft_karp(int nr_noduri_R, vector<pair<int, int>>& cuplaje);
//functie care ia un graf bipartit si care returneaza cuplajul maxim al
//acestui graf
};
Graf::Graf(){
nr_noduri = 0;
nr_muchii = 0;
}
void Graf::citire_neorientat(const string fis){
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2;
fi >> nod_1 >> nod_2;
lista_adiacenta[nod_1].push_back(nod_2);
lista_adiacenta[nod_2].push_back(nod_1);
}
fi.close();
}
void Graf::citire_orientat(const string fis){
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2;
fi >> nod_1 >> nod_2;
lista_adiacenta[nod_1].push_back(nod_2);
}
fi.close();
}
int Graf::citire_orientat_bfs(const string fis){
ifstream fi(fis);
int nod_start;
fi >> nr_noduri >> nr_muchii >> nod_start;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2;
fi >> nod_1 >> nod_2;
lista_adiacenta[nod_1].push_back(nod_2);
}
fi.close();
return nod_start;
}
vector<vector<int>> Graf::citire_neorientat_cost(const string fis){
vector<vector<int>> muchii_ponderate;
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
muchii_ponderate.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2, cost;
fi >> nod_1 >> nod_2 >> cost;
lista_adiacenta[nod_1].push_back(nod_2);
lista_adiacenta[nod_2].push_back(nod_1);
//punem datele citite in forma [cost, nod_1, nod_2] ca sa nu fie
//nevoie de un criteriu de sort (se va face o sortare directa dupa
//primul element)
muchii_ponderate[i].push_back(cost);
muchii_ponderate[i].push_back(nod_1);
muchii_ponderate[i].push_back(nod_2);
}
fi.close();
return muchii_ponderate;
}
void Graf::citire_orientat_cost(const string fis){
//retinem muchiile cu costuri ca un vector de vectori de perechi de forma
//muchii_ponderate[nod_1] -> (nod_2, cost) pentru a gasi eficient muchiile
//necesare
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
muchii_cu_costuri.push_back(vector<pair<int, int>>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2, cost;
fi >> nod_1 >> nod_2 >> cost;
muchii_cu_costuri[nod_1].push_back(make_pair(nod_2, cost));
}
fi.close();
}
vector<unordered_set<int>> Graf::citire_retea(const string fis){
//retinem muchiile cu costuri ca un vector de vectori de perechi de forma
//muchii_ponderate[nod_1] -> (nod_2, cost) pentru a gasi eficient muchiile
//necesare; de asemenea, vom pune muchiile intr-un vector de hash-uri
//pentru a gasi arcele inverse ale unui nod
vector<unordered_set<int>> lista_arce;
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
muchii_cu_costuri.push_back(vector<pair<int, int>>());
lista_arce.push_back(unordered_set<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2, cost;
fi >> nod_1 >> nod_2 >> cost;
muchii_cu_costuri[nod_1].push_back(make_pair(nod_2, cost));
lista_arce[nod_1].insert(nod_2);
}
fi.close();
return lista_arce;
}
vector<vector<int>> Graf::citire_orientat_cost_matrice(const string fis){
vector<vector<int>> muchii_ponderate;
ifstream fi(fis);
fi >> nr_noduri;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 1; i <= nr_noduri + 1; i++){
muchii_ponderate.push_back(vector<int>());
}
for (int i = 0; i < nr_noduri; i++){
for (int j = 0; j < nr_noduri; j++){
int dist;
fi >> dist;
if (dist){
lista_adiacenta[i].push_back(j);
}
muchii_ponderate[i].push_back(dist);
}
}
fi.close();
return muchii_ponderate;
}
void Graf::citire_arbore(const string fis){
ifstream fi(fis);
fi >> nr_noduri;
nr_muchii = nr_noduri - 1;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2;
fi >> nod_1 >> nod_2;
lista_adiacenta[nod_1].push_back(nod_2);
lista_adiacenta[nod_2].push_back(nod_1);
}
fi.close();
}
vector<vector<int>> Graf::citire_orientat_cost_hamilton(const string fis){
//vom retine costurile muchiilor separat, din cauza retinerii diferite a
//listei de adiacenta
vector<vector<int>> costuri_muchii;
ifstream fi(fis);
fi >> nr_noduri >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 1; i <= nr_noduri + 1; i++){
costuri_muchii.push_back(vector<int>());
for (int j = 1; j <= nr_noduri + 1; j++){
costuri_muchii[i - 1].push_back(maxim);
}
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2, cost;
fi >> nod_1 >> nod_2 >> cost;
//diferenta fata de citire_orientat_cost este ca in lista de adiacenta
//se vor pune arcele "inverse", deoarece algoritmul va folosi lista
//pentru a cauta, dat fiind un nod, nodurile care au arc spre el.
lista_adiacenta[nod_2].push_back(nod_1);
costuri_muchii[nod_1][nod_2] = cost;
}
fi.close();
return costuri_muchii;
}
int Graf::citire_bipartit(const string fis){
ifstream fi(fis);
//vom citi si numarul de noduri din multimea R, pe care il vom returna
int nr_noduri_2;
fi >> nr_noduri >> nr_noduri_2 >> nr_muchii;
for (int i = 1; i <= nr_noduri + 1; i++){
lista_adiacenta.push_back(vector<int>());
}
for (int i = 0; i < nr_muchii; i++){
int nod_1, nod_2;
fi >> nod_1 >> nod_2;
lista_adiacenta[nod_1].push_back(nod_2);
}
fi.close();
return nr_noduri_2;
}
vector<int> Graf::BFS(int nod_start){
//declaram si initializam un vector "vizitat" pentru marcarea nodurilor
//vizitate, o coada "coada_varfuri" pentru prelucrarea in ordine a
//nodurilor parcurse, un vector "nivel" care stocheaza nivelurile pe care
//se afla fiecare nod si o variabila "nod" pentru nodul care trebuie
//prelucrat
vector<bool> vizitat(nr_noduri + 1, false);
queue<int> coada_varfuri;
vector<int> nivel(nr_noduri + 1, nul);
//incepem prin a pune nodul de start in coada, marcandu-l drept vizitat
//si setandu-i nivelul ca fiind 0
coada_varfuri.push(nod_start);
vizitat[nod_start] = true;
nivel[nod_start] = 0;
//facem BFS pana cand nu mai sunt noduri de prelucrat
while (!coada_varfuri.empty()){
//luam un nod din coada
int nod = coada_varfuri.front();
//pentru fiecare vecin al sau
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
//daca vecinul respectiv nu a mai fost vizitat, il punem in
//coada, il marcam drept vizitat si ii setam nivelul ca fiind
//cel al nodului initial + 1
if (!vizitat[*i]){
coada_varfuri.push(*i);
vizitat[*i] = true;
nivel[*i] = nivel[nod] + 1;
}
}
//eliminam nodul din coada
coada_varfuri.pop();
}
return nivel;
}
void Graf::DFS(int nod, vector<bool>& vizitat){
//marcam nodul curent ca fiind vizitat
vizitat[nod] = true;
//pentru fiecare vecin al sau
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
//daca vecinul respectiv nu a mai fost vizitat, apelam DFS pe acesta
if (!vizitat[*i]){
DFS(*i, vizitat);
}
}
}
int Graf::componente_conexe(){
//declaram si initializam un vector "vizitat" pentru marcarea nodurilor
//vizitate si o variabila "nr_comp_conexe" in care stocam numarul de
//componente conexe gasite (egal cu numarul de DFS-uri apelate din
//aceasta functie)
vector<bool> vizitat(nr_noduri + 1, false);
int nr_comp_conexe = 0;
//luam fiecare nod, si daca nu a mai fost vizitat, apelam DFS pornind
//de la el si incrementam numarul de componente conexe gasite
for (int i = 1; i <= nr_noduri; i++){
if (!vizitat[i]){
nr_comp_conexe++;
DFS(i, vizitat);
}
}
//la sfarsit, afisam numarul de componente conexe cerut
return nr_comp_conexe;
}
void Graf::gasire_componenta_biconexa(int nod1, int nod2, vector<vector<int>>& comp_biconexe, stack<pair<int, int>>& muchii_comp_biconexe){
//eliminam muchii din stiva pana ajungem la muchia (nod1, nod2)
//cream un vector in care retinem componenta biconexa (muchiile sale,
//mai tarziu vom pastra doar nodurile)
vector<int> comp;
int n1, n2;
do {
n1 = muchii_comp_biconexe.top().first;
n2 = muchii_comp_biconexe.top().second;
comp.push_back(n1);
comp.push_back(n2);
//stergem muchia din stiva
muchii_comp_biconexe.pop();
} while (n1 != nod1 && n2 != nod2);
//punem componenta in lista de componente
comp_biconexe.push_back(comp);
}
void Graf::cautare_componente_biconexe(int nod_curent, int nod_initial, int nv, vector<int>& nivel, vector<int>& low, vector<vector<int>>& comp_biconexe, stack<pair<int, int>>& muchii_comp_biconexe){
//setam adancimea si low[nod_curent]; initial, nodul curent are low
//egal cu adancimea
if (nivel.empty() && low.empty()){
for (int i = 1; i <= nr_noduri + 1; i++){
nivel.push_back(nul);
low.push_back(nul);
}
}
nivel[nod_curent] = nv;
low[nod_curent] = nv;
//pentru fiecare vecin al nodului
for (auto i = lista_adiacenta[nod_curent].begin(); i != lista_adiacenta[nod_curent].end(); i++){
//daca intalnim nodul initial printre vecini, il ignoram
if (*i == nod_initial){
continue;
}
//daca intalnim un nod pe care nu l-am vizitat
if (nivel[*i] == nul){
//punem muchia dintre cele doua noduri in stiva
muchii_comp_biconexe.push(make_pair(nod_curent, *i));
//apelam DFS (reapelam aceasta functie) pe nodul gasit
//nv va avea valoarea nv + 1
cautare_componente_biconexe(*i, nod_curent, nv + 1, nivel, low, comp_biconexe, muchii_comp_biconexe);
//dupa ce iesim din recursie, va trebui sa actualizam
//low[nod_curent] cu low-ul nodului gasit
low[nod_curent] = min(low[nod_curent], low[*i]);
//daca nodul gasit nu poate ajunge la un stramos al nodului curent
if (low[*i] >= nivel[nod_curent]){
//am gasit o componenta biconexa, deci incepem sa o retinem
gasire_componenta_biconexa(nod_curent, *i, comp_biconexe, muchii_comp_biconexe);
}
} else {
//altfel, actualizam low[nod_curent] cu adancimea nodului gasit
low[nod_curent] = min(low[nod_curent], nivel[*i]);
}
}
}
vector<vector<int>> Graf::componente_biconexe(){
//declaram un vector nivel care stocheaza adancimea la care a fost gasit
//un nod (sau -1 daca este nevizitat), un vector low care stocheaza
//nivelul minim la care poate ajunge un anumit nod mergand in fii pe
//drumuri de intoarcere, un vector de vectori comp_biconexe care
//stocheaza componentele biconexe si o stiva de perechi
//muchii_comp_biconexe care stocheaza muchii care vor intra intr-o
//viitoare componenta biconexa
vector<int> nivel;
vector<int> low;
vector<vector<int>> comp_biconexe;
stack<pair<int, int>> muchii_comp_biconexe;
//pornim din nodul 1, care va avea adancimea 0
cautare_componente_biconexe(1, 0, 0, nivel, low, comp_biconexe, muchii_comp_biconexe);
return comp_biconexe;
}
void Graf::DFS_tare_conexe(int nod, int& index_ctc, vector<int>& index, vector<int>& low, vector<bool>& inComponenta, stack<int>& noduri_comp_tare_conexa, vector<vector<int>>& comp_tare_conexe){
//initializam nodul si low[nod] cu index-ul curent, punem nodul in stiva,
//ii marcam prezenta in stiva si setam indexul pentru urmatorul nod
index[nod] = index_ctc;
low[nod] = index_ctc;
index_ctc++;
noduri_comp_tare_conexa.push(nod);
inComponenta[nod] = true;
//pentru fiecare vecin al nodului
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
//daca nodul nu a fost vizitat, apelam DFS pe acest nod si actualizam
//low-ul nodului curent cu low-ul nodului gasit
if (index[*i] == nul){
DFS_tare_conexe(*i, index_ctc, index, low, inComponenta, noduri_comp_tare_conexa, comp_tare_conexe);
low[nod] = min(low[nod], low[*i]);
} else if (inComponenta[*i]){
//altfel, daca nodul este in stiva (si deci in componenta curenta),
//actualizam low-ul nodului curent cu indexul nodului gasit
low[nod] = min(low[nod], index[*i]);
}
}
//daca low-ul unui nod este egal cu indexul sau, atunci inseamna ca el e
//nodul "radacina" si ca trebuie sa formam componenta tare conexa din
//acest nod
if (low[nod] == index[nod]){
//cream vectorul cu nodurile din componenta tare conexa extragandu-le
//din stiva si stergandu-le prezenta in stiva; adaugam apoi acest
//vector printre componente
vector<int> comp;
int n;
do {
n = noduri_comp_tare_conexa.top();
comp.push_back(n);
inComponenta[n] = false;
noduri_comp_tare_conexa.pop();
} while (n != nod);
comp_tare_conexe.push_back(comp);
}
}
vector<vector<int>> Graf::componente_tare_conexe(){
int index_ctc = 0;
vector<int> index(nr_noduri + 1, nul);
vector<int> low(nr_noduri + 1, nul);
vector<bool> inComponenta(nr_noduri + 1, false);
stack<int> noduri_comp_tare_conexa;
vector<vector<int>> comp_tare_conexe;
for (int i = 1; i <= nr_noduri; i++){
if (index[i] == nul){
DFS_tare_conexe(i, index_ctc, index, low, inComponenta, noduri_comp_tare_conexa, comp_tare_conexe);
}
}
return comp_tare_conexe;
}
void Graf::havel_hakimi(vector<int>& secv_grade, int n, const string fis){
//declaram o variabila suma_grade pentru a verifica daca suma gradelor
//este para sau nu
int suma_grade = 0;
ofstream fo(fis);
for (int i = 0; i < n; i++){
//verificam daca un grad este mai mare decat n, daca da atunci
//nu se poate construi graful cerut
if (secv_grade[i] > n - 1){
fo << "NU";
fo.close();
return;
}
//altfel, adunam gradul la suma pentru a doua conditie necesara
suma_grade += secv_grade[i];
}
//daca suma gradelor este impara, nu se poate construi graful cerut
if (suma_grade % 2 != 0){
fo << "NU";
fo.close();
return;
}
//sortam descrescator vectorul de grade pentru a alege cel mai mare grad
//din secventa
sort(secv_grade.begin(), secv_grade.end(), greater<int>());
//algoritmul ruleaza cat timp secventa contine valori nenule
//vectorul fiind sortat descrescator, daca primul element este nul,
//inseamna ca si celelalte elemente sunt nule
while (secv_grade[0] > 0){
//scadem gradele pentru atatea noduri cat este gradul nodului curent
for (int i = 1; i <= secv_grade[0]; i++){
secv_grade[i]--;
//daca un nod, prin aceasta scadere, ajunge sa aiba grad negativ,
//atunci nu se poate construi graful cerut
if (secv_grade[i] < 0){
fo << "NU";
fo.close();
return;
}
}
//eliminam gradul prelucrat
secv_grade.erase(secv_grade.begin());
//sortam din nou vectorul descrescator pentru a alege, in continuare,
//cel mai mare grad din secventa
sort(secv_grade.begin(), secv_grade.end(), greater<int>());
}
//daca programul nu a fost intrerupt pana acum, inseamna ca putem construi
//graful cerut
fo << "DA";
fo.close();
}
vector<int> Graf::sort_top(){
//declaram un vector grade_interne care stocheaza gradul intern al
//fiecarui nod, si punem lungimea listei de adiacenta intr-o variabila
vector<int> grade_interne(nr_noduri + 1, 0);
int lungime_lista_ad = lista_adiacenta.size();
//cream vectorul de grade interne luand toate muchiile din lista de
//adiacenta
for (int i = 1; i <= lungime_lista_ad; i++){
for (auto j = lista_adiacenta[i].begin(); j != lista_adiacenta[i].end(); j++){
grade_interne[*j]++;
}
}
//declaram o coada care stocheaza nodurile de prelucrat (ca la BFS)
//initial, punem in coada nodurile cu gradul intern 0
queue<int> coada_varfuri;
for (int i = 1; i <= nr_noduri; i++){
if (grade_interne[i] == 0){
coada_varfuri.push(i);
}
}
//declaram vectorul sortare, care stocheaza sortarea topologica ceruta
vector<int> sortare;
//procesarea nodurilor are loc cat timp exista noduri in coada
while (!coada_varfuri.empty()){
//preluam un nod din coada, il adaugam in sortare si apoi il
//stergem din coada
int nod = coada_varfuri.front();
sortare.push_back(nod);
coada_varfuri.pop();
//parcurgem toate muchiile care pornesc din nodul respectiv si
//scadem gradul intern al nodurilor legate de acesta
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
grade_interne[*i]--;
//daca gradul intern al unui nod ajunge 0 prin aceasta scadere,
//introducem nodul in coada
if (grade_interne[*i] == 0){
coada_varfuri.push(*i);
}
}
}
return sortare;
}
void Graf::DFS_muchii_critice(int nod, vector<int>& nivel, vector<int>& low, vector<vector<int>>& lista_muchii){
//daca nivelul nodului curent este nul, inseamna ca acesta este primul
//apel al functiei, deci initializam acest nivel cu 0
if (nivel[nod] == nul){
nivel[nod] = 0;
}
//initial, low[nod] este egal cu nivelul nodului
low[nod] = nivel[nod];
//parcurgem muchiile care pornesc din acest nod
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
//daca am gasit un nod nevizitat
if (nivel[*i] == nul){
//initializam nivelul nodului pe care urmeaza sa apelam DFS cu
//nivelul nodului initial + 1
nivel[*i] = nivel[nod] + 1;
//apelam DFS pe nodul respectiv
DFS_muchii_critice(*i, nivel, low, lista_muchii);
//dupa apelul recursiv, verificam daca nodul gasit a putut urca
//mai sus de nodul initial; daca nu, am gasit o muchie critica
//pe care o punem in vectorul de muchii
if (low[*i] > nivel[nod]){
vector<int> muchie;
muchie.push_back(nod);
muchie.push_back(*i);
lista_muchii.push_back(muchie);
}
//deoarece nodul curent poate urca la acelasi nivel ca nodul
//gasit mergand in jos, actualizam low[nod] cu low-ul celui gasit
low[nod] = min(low[nod], low[*i]);
}
else if (nivel[*i] < nivel[nod] - 1){
//daca am gasit un nod vizitat care se afla mai sus de nodul
//curent, am gasit o muchie de intoarcere si actualizam low[nod]
//cu nivelul nodului gasit
low[nod] = min(low[nod], nivel[*i]);
}
}
}
vector<vector<int>> Graf::muchii_critice(){
//declaram un vector "nivel" care stocheaza nivelele la care se afla
//nodurile, un vector "low" care stocheaza cat de sus poate merge un nod
//printr-o muchie de intoarcere si un vector "lista_muchii" care
//stocheaza muchiile cerute
vector<int> nivel(nr_noduri + 1, nul);
vector<int> low(nr_noduri + 1, nul);
vector<vector<int>> lista_muchii;
//apelam DFS pe nodul de start 0
DFS_muchii_critice(0, nivel, low, lista_muchii);
return lista_muchii;
}
int Graf::reprez_Kruskal(int nod, vector<int>& tata){
while (tata[nod] != 0){
nod = tata[nod];
}
return nod;
}
void Graf::reunire_Kruskal(int nod1, int nod2, vector<int>& tata, vector<int>& inaltime){
int repr_nod1 = reprez_Kruskal(nod1, tata);
int repr_nod2 = reprez_Kruskal(nod2, tata);
//legam componenta cu inaltimea mai mica de cea cu inaltimea mai mare
//daca ambele componente au aceeasi inaltime, inaltimea componentei finale
//va creste cu 1
if (inaltime[repr_nod1] > inaltime[repr_nod2]){
tata[repr_nod2] = repr_nod1;
} else {
tata[repr_nod1] = repr_nod2;
if (inaltime[repr_nod1] == inaltime[repr_nod2]){
inaltime[repr_nod2]++;
}
}
}
vector<vector<int>> Graf::apm_Kruskal(vector<vector<int>>& muchii_ponderate, int& cost_apm){
//declaram un vector de tati si un vector de inaltimi, pentru reuniuni si
//gasiri de reprezentanti eficiente
vector<int> tata(nr_noduri + 1, 0);
vector<int> inaltime(nr_noduri + 1, 0);
vector<vector<int>> apm;
for (int i = 1; i < nr_noduri; i++){
apm.push_back(vector<int>());
}
//sortam muchiile dupa cost (deoarece elementele de indice 0 sunt
//costurile, nu este nevoie de un criteriu de sort)
sort(muchii_ponderate.begin(), muchii_ponderate.end());
//declaram o variabila care spune cate muchii am selectat pana acum
int muchii_selectate = 0;
for (int i = 0; i < nr_muchii; i++){
//punem cele doua noduri care formeaza muchia curenta in cate o
//variabila
int nod1 = muchii_ponderate[i][1];
int nod2 = muchii_ponderate[i][2];
//daca nodurile nu fac parte din aceeasi componenta, atunci punem
//muchia in APM, adunam costul muchiei la cel al APM-ului, reunim
//cele doua componente si actualizam numarul de muchii selectate
if (reprez_Kruskal(nod1, tata) != reprez_Kruskal(nod2, tata)){
apm[muchii_selectate].push_back(nod1);
apm[muchii_selectate].push_back(nod2);
cost_apm += muchii_ponderate[i][0];
reunire_Kruskal(nod1, nod2, tata, inaltime);
muchii_selectate++;
//daca am selectat atatea muchii cat ar fi nevoie pentru un
//arbore, atunci APM-ul a fost creat si il returnam
if (muchii_selectate == nr_noduri - 1){
return apm;
}
}
}
return apm;
}
int Graf::reprez_paduri(int nod, vector<int>& tata){
//diferenta fata de functia reprez a algoritmului lui Kruskal este ca
//vom optimiza gasirea reprezentantilor prin procedeul compresiei de cale
//legam toate nodurile de pe lantul din care face parte nodul dat direct
//de radacina (adica egalam tatal lor cu radacina)
if (tata[nod] == 0){
return nod;
}
tata[nod] = reprez_paduri(tata[nod], tata);
return tata[nod];
}
void Graf::reunire_multimi_paduri(int nod1, int nod2, vector<int>& tata, vector<int>& inaltime){
//functie aproape identica cu cea de reunire a algoritmului Kruskal, cu
//exceptia ca vom folosi functia reprez_paduri in loc de reprez_Kruskal
int repr_nod1 = reprez_paduri(nod1, tata);
int repr_nod2 = reprez_paduri(nod2, tata);
if (inaltime[repr_nod1] > inaltime[repr_nod2]){
tata[repr_nod2] = repr_nod1;
} else {
tata[repr_nod1] = repr_nod2;
if (inaltime[repr_nod1] == inaltime[repr_nod2]){
inaltime[repr_nod2]++;
}
}
}
void Graf::paduri_disjuncte(const string fis_in, const string fis_out){
//algoritmul pentru paduri de multimi disjuncte este foarte similar cu
//cel al lui Kruskal pentru APM, cu functia reprez modificata
int nr_op;
ifstream fi(fis_in);
ofstream fo(fis_out);
fi >> nr_noduri >> nr_op;
//declaram un vector de tati si un vector de inaltimi, pentru reuniuni si
//gasiri de reprezentanti eficiente
vector<int> tata(nr_noduri + 1, 0);
vector<int> inaltime(nr_noduri + 1, 0);
for (int i = 0; i < nr_op; i++){
int tip_op;
int nod1, nod2;
fi >> tip_op >> nod1 >> nod2;
//daca operatia ceruta este de tip 1, reunim direct cele doua multimi
//din care fac parte nodurile (nu este nevoie in acest caz sa
//verificam daca nodurile fac parte din multimi diferite)
if (tip_op == 1){
reunire_multimi_paduri(nod1, nod2, tata, inaltime);
} else if (tip_op == 2){
//altfel, daca operatia ceruta este de tip 2, ni se cere sa
//aflam daca cele doua noduri au acelasi reprezentant. Afisam
//corespunzator in functie de raspunsul la aceasta intrebare.
if (reprez_paduri(nod1, tata) == reprez_paduri(nod2, tata)){
fo << "DA\n";
} else {
fo << "NU\n";
}
}
}
fi.close();
fo.close();
}
vector<int> Graf::dijkstra(){
//declaram un vector "distanta" care memoreaza distantele de la nodul 1 la
//celelalte noduri, un vector "vizitat" care retine ce noduri sunt
//vizitate si un heap care va retine perechi de forma (distanta minima,
//nod) pentru selectarea nodurilor in timpul algoritmului
vector<int> distanta(nr_noduri + 1, maxim);
vector<bool> vizitat(nr_noduri + 1, false);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap_noduri;
//deoarece distanta de la nodul 1 la el insusi este 0, o setam astfel in
//vectorul distanta
distanta[1] = 0;
//initializam heapul, punand elementul primului nod cu distanta 0
heap_noduri.push(make_pair(0, 1));
//cat timp avem noduri de prelucrat
while (!heap_noduri.empty()){
//extragem un nod
int nod = heap_noduri.top().second;
heap_noduri.pop();
//marcam nodul extras drept vizitat
vizitat[nod] = true;
int nr_noduri_adiacente = muchii_cu_costuri[nod].size();
for (int i = 0; i < nr_noduri_adiacente; i++){
int nod_2 = muchii_cu_costuri[nod][i].first;
int cost_muchie = muchii_cu_costuri[nod][i].second;
//daca am gasit o distanta mai mica de la nodul 1 la nod_2 prin
//nod, iar nod_2 nu a mai fost vizitat, actualizam distanta si
//tatal lui nod_2 corespunzator si punem nod_2 in heap pentru a
//fi prelucrat
if ((!vizitat[nod_2]) && (distanta[nod] + cost_muchie < distanta[nod_2])){
distanta[nod_2] = distanta[nod] + cost_muchie;
heap_noduri.push(make_pair(distanta[nod_2], nod_2));
}
}
}
return distanta;
}
vector<int> Graf::bellman_ford(bool& exista_ciclu, const string fis){
//declaram un vector "distanta" care memoreaza distantele de la nodul 1 la
//celelalte noduri, un vector "tata" care retine tatii nodurilor, un heap
//care va retine perechi de forma (distanta minima, nod) pentru selectarea
//nodurilor in timpul algoritmului si o coada care va retine ordinea in
//care vor fi selectate nodurile la o anumita etapa
vector<int> distanta(nr_noduri + 1, maxim);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap_noduri;
queue<int> coada_noduri;
//deoarece distanta de la nodul 1 la el insusi este 0, o setam astfel in
//vectorul distanta
distanta[1] = 0;
//deoarece in primul pas trebuie sa alegem toate nodurile, punem in coada
//toate nodurile
for (int i = 1; i <= nr_noduri; i++){
coada_noduri.push(i);
}
//vom avea cel mult nr_noduri - 1 etape (pentru testarea de ciclu negativ)
for (int i = 1; i < nr_noduri; i++){
//scoatem noduri din coada pana cand nu vom mai avea noduri in ea
while (!coada_noduri.empty()){
int nod = coada_noduri.front();
coada_noduri.pop();
int nr_noduri_adiacente = muchii_cu_costuri[nod].size();
for (int j = 0; j < nr_noduri_adiacente; j++){
int nod_2 = muchii_cu_costuri[nod][j].first;
int cost_muchie = muchii_cu_costuri[nod][j].second;
//daca am gasit o distanta mai mica intre nod_2 si nodul 1
//prin nod, actualizam distanta si tatal lui nod_2
//corespunzator si punem in heap nod_2 cu noua lui distanta
if (distanta[nod] + cost_muchie < distanta[nod_2]){
distanta[nod_2] = distanta[nod] + cost_muchie;
heap_noduri.push(make_pair(distanta[nod_2], nod_2));
}
}
}
//punem noduri in coada in ordinea in care au fost sortate de heap
//(dupa cost) si golim heap-ul treptat pentru adaugarea de noi noduri
//actualizate
while (!heap_noduri.empty()){
coada_noduri.push(heap_noduri.top().second);
heap_noduri.pop();
}
}
return distanta;
}
bool Graf::BFS_construire_lant(vector<int>& tata, vector<vector<pair<int, int>>>& fluxuri, vector<unordered_set<int>>& lista_arce){
//resetam vectorul tata pentru a incepe un nou BFS
for (int i = 1; i <= nr_noduri; i++){
tata[i] = 0;
}
vector<bool> vizitat(nr_noduri + 1, false);
queue<int> coada_noduri;
coada_noduri.push(1);
while (!coada_noduri.empty()){
int nod = coada_noduri.front();
//cautam arcele directe, lucrand direct pe vectorul de muchii
int nr_noduri_adiacente = muchii_cu_costuri[nod].size();
for (int i = 0; i < nr_noduri_adiacente; i++){
//preluam un nod adiacent
int nod_2 = muchii_cu_costuri[nod][i].first;
//daca nu am mai vizitat nodul gasit si mai putem trimite flux pe
//muchia dintre nod initial si cel adiacent, punem nodul gasit in
//coada
if ((!vizitat[nod_2]) && (muchii_cu_costuri[nod][i].second > fluxuri[nod][i].second)){
coada_noduri.push(nod_2);
vizitat[nod_2] = true;
tata[nod_2] = nod;
//daca am ajuns la nodul destinatie, am construit lantul cerut
if (nod_2 == nr_noduri){
return true;
}
}
}
//cautam arcele inverse, cautand posibile muchii intre toate nodurile
//si nodul luat din coada
for (int i = 1; i <= nr_noduri; i++){
//daca am gasit muchia dintre nodul i si nodul din coada in
//vectorul de hash-uri, putem sa o cautam si in vectorul de
//muchii
if ((i != nod) && (lista_arce[i].find(nod) != lista_arce[i].end())){
int j = 0;
while (muchii_cu_costuri[i][j].first != nod){
j++;
}
//daca putem scoate flux din muchie, adaugam nodul i in coada
//si setam tatal ca fiind -(nod) pentru a marca faptul ca
//avem un arc invers intre cele doua noduri
if ((!vizitat[i]) && (fluxuri[i][j].second > 0)){
coada_noduri.push(i);
vizitat[i] = true;
tata[i] = (-1) * (nod);
//daca nodul i este cel destinatie, am construit lantul
//cerut
if (i == nr_noduri){
return true;
}
}
}
}
coada_noduri.pop();
}
return false;
}
int Graf::revizuire_flux(vector<int>& tata, vector<vector<pair<int, int>>>& fluxuri){
//initial, fluxul cu care revizuim lantul va fi un numar INF (maxim)
int flux_adaugat = maxim;
//calculam fluxul, reconstituind drumul invers, de la destinatie la sursa
for (int nod = nr_noduri; nod != 1; nod = tata[nod]){
int i = 0;
int nod_2 = tata[nod];
while (muchii_cu_costuri[nod_2][i].first != nod){
i++;
}
//fluxul va fi minimul dintre fluxul gasit pana in acest moment si
//fluxul care se mai poate adauga pe muchia curenta
flux_adaugat = min(flux_adaugat, muchii_cu_costuri[nod_2][i].second - fluxuri[nod_2][i].second);
}
//modificam fluxul pe lant
for (int nod = nr_noduri; nod != 1; nod = tata[nod]){
int i = 0;
int nod_2 = tata[nod];
//daca arcul este direct, adaugam fluxul gasit anterior la fluxul
//arcului
if (nod_2 > 0){
while (muchii_cu_costuri[nod_2][i].first != nod){
i++;
}
fluxuri[nod_2][i].second += flux_adaugat;
} else {
//pentru arce inverse, scadem fluxul gasit anterior la fluxul
//arcului
nod_2 *= -1;
while (muchii_cu_costuri[nod][i].first != nod_2){
i++;
}
fluxuri[nod][i].second -= flux_adaugat;
}
}
return flux_adaugat;
}
int Graf::edmonds_karp(vector<unordered_set<int>>& lista_arce){
vector<vector<pair<int, int>>> fluxuri;
vector<int> tata(nr_noduri + 1, 0);
for (int i = 1; i <= nr_noduri + 1; i++){
fluxuri.push_back(vector<pair<int, int>>());
}
//initial, vom avea fluxul 0 pe toate arcele
for (int i = 1; i <= nr_noduri; i++){
int nr_noduri_adiacente = muchii_cu_costuri[i].size();
for (int j = 0; j < nr_noduri_adiacente; j++){
fluxuri[i].push_back(make_pair(muchii_cu_costuri[i][j].first, 0));
}
}
//fluxul cerut va fi initial 0
int flux_total = 0;
//cat timp putem construi lanturi pe care putem adauga flux, modificam
//fluxul pe aceste lanturi
while (BFS_construire_lant(tata, fluxuri, lista_arce)){
int flux = revizuire_flux(tata, fluxuri);
//adaugam fluxul gasit pe fiecare lant la fluxul total cerut
flux_total += flux;
}
return flux_total;
}
vector<vector<int>> Graf::floyd_warshall(vector<vector<int>>& muchii_ponderate){
vector<vector<int>> distante_minime;
for (int i = 1; i <= nr_noduri; i++){
distante_minime.push_back(vector<int>());
}
for (int i = 0; i < nr_noduri; i++){
for (int j = 0; j < nr_noduri; j++){
//initial, distantele minime vor fi cele directe de la nodul i la
//nodul j
distante_minime[i].push_back(muchii_ponderate[i][j]);
}
}
//cautam distante minime de la toate nodurile i la toate nodurile j prin
//nodul k
for (int k = 0; k < nr_noduri; k++){
for (int i = 0; i < nr_noduri; i++){
for (int j = 0; j < nr_noduri; j++){
//daca gasim o distanta mai mica decat cea curenta prin nodul
//k, atunci actualizam distanta curenta
if (distante_minime[i][j] > distante_minime[i][k] + distante_minime[k][j]){
distante_minime[i][j] = distante_minime[i][k] + distante_minime[k][j];
}
}
}
}
return distante_minime;
}
void Graf::BFS_diametru_arbore(int nod_start, int& ultim, int& diametru){
//algoritmul este identic cu cel de BFS, cu exceptia faptului ca utilizam
//o variabila care stocheaza ultimul nod parcurs, "ultim", si o variabila
//care stocheaza diametrul cerut dupa doua apelari ale acestei functii,
//"diametru"
vector<bool> vizitat(nr_noduri + 1, false);
queue<int> coada_varfuri;
vector<int> nivel(nr_noduri + 1, nul);
coada_varfuri.push(nod_start);
vizitat[nod_start] = true;
nivel[nod_start] = 1;
while (!coada_varfuri.empty()){
int nod = coada_varfuri.front();
for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
if (!vizitat[*i]){
coada_varfuri.push(*i);
vizitat[*i] = true;
nivel[*i] = nivel[nod] + 1;
//actualizam ultimul nod gasit si diametrul dupa fiecare nou
//nod nevizitat gasit; diametrul va fi nivelul nodului gasit
//(care, in final, va fi nivelul ultimului nod gasit in BFS-ul
//care porneste din ultimul nod gasit in primul apel)
ultim = *i;
diametru = nivel[*i];
}
}
coada_varfuri.pop();
}
}
vector<int> Graf::gasire_ciclu_euler(){
//declaram o stiva de noduri pentru procesarea nodurilor si un vector
//"vizitat" care stocheaza care muchii au fost vizitate
vector<int> ciclu;
stack<int> stiva_noduri;
vector<vector<bool>> vizitat;
for (int i = 1; i <= nr_noduri + 2; i++){
vizitat.push_back(vector<bool>(nr_noduri + 2, false));
}
//la inceput, punem nodul 1 in stiva
stiva_noduri.push(1);
//scoatem noduri din stiva cat timp avem noduri de procesat
while (!stiva_noduri.empty()){
int nod = stiva_noduri.top();
int nod_2;
int nr_muchii_adiacente = lista_adiacenta[nod].size();
int i = 0;
bool gasit_muchie = false;
//cautam o muchie adiacenta si nevizitata pentru nodul dat
while (i < nr_muchii_adiacente && !gasit_muchie){
//daca am gasit o astfel de muchie, marcam acest fapt, altfel
//continuam cautarea
if (!vizitat[nod][i]){
nod_2 = lista_adiacenta[nod][i];
vizitat[nod][i] = true;
gasit_muchie = true;
} else {
i++;
}
}
//daca am gasit muchia data, o marcam drept vizitata (fata de ambele
//noduri)
if (gasit_muchie){
int j = 0;
while ((lista_adiacenta[nod_2][j] != nod) || (vizitat[nod_2][j])){
j++;
}
vizitat[nod_2][j] = true;
//punem celalalt nod al muchiei in stiva pentru procesare
stiva_noduri.push(nod_2);
} else {
//daca nu mai gasim astfel de muchii, s-a terminat procesarea
//nodului, deci il scoatem din stiva si il punem in ciclul cerut
stiva_noduri.pop();
ciclu.push_back(nod);
}
}
return ciclu;
}
void Graf::ciclu_euler(vector<int>& ciclu_eulerian, const string fis){
//verificam conditia de ciclu eulerian (daca toate nodurile au grad par)
for (int nod = 1; nod <= nr_noduri; nod++){
int grad_nod = lista_adiacenta[nod].size();
//daca un nod are grad impar, afisam ca nu are solutie si ne oprim
if (grad_nod % 2 != 0){
ofstream fo(fis);
fo << "-1";
fo.close();
return;
}
}
//altfel, incepem algoritmul de gasire a ciclului
ciclu_eulerian = gasire_ciclu_euler();
}
int Graf::ciclu_hamilton(vector<vector<int>>& costuri_muchii){
//declaram o matrice care va memora costul minim al unui nod dat, data
//fiind o ruta compusa din anumite noduri.
//ruta va fi exprimata printr-un numar de la 0 pana la 2^nr_noduri - 1,
//care, in scrierea sa in baza 2, marcheaza ce noduri fac parte din ruta.
//de exemplu, 5 este configuratia rutei compusa din nodurile 0 si 2,
//deoarece 5 = (101)2 = 2^0 + 2^2.
vector<vector<int>> cost_cu_ruta_nod;
for (int i = 0; i < (1 << nr_noduri); i++){
cost_cu_ruta_nod.push_back(vector<int>(nr_noduri, maxim));
}
//initializam costul minim pana la nodul 0 pe ruta cu nodul 0 ca fiind 0.
cost_cu_ruta_nod[1][0] = 0;
//parcurgem toate rutele posibile si incercam sa calculam costul minim
//pentru fiecare nod prin aceste rute
for (int ruta = 0; ruta < (1 << nr_noduri); ruta++){
for (int nod = 0; nod < nr_noduri; nod++){
//procesam toate nodurile care fac parte din ruta curenta
//daca un nod apartine unei rute, si-ul pe biti dintre numarul
//rutei si 2^nod va fi nenul
if (ruta & (1 << nod)){
//procesam toate nodurile care au muchie spre nodul gasit
//(tinem lista de adiacenta "invers" cu acest scop inca de la
//citire); le vom numi "noduri adiacente" in explicatiile ce
//urmeaza
int nr_muchii_adiacente = lista_adiacenta[nod].size();
for (int i = 0; i < nr_muchii_adiacente; i++){
//daca un nod adiacent exista in ruta, putem actualiza
//costul minim al nodului "nod" pe aceasta ruta
if (ruta & (1 << lista_adiacenta[nod][i])){
//actualizam costul minim, care va fi minimul dintre
//costul gasit pana in acest punct si suma dintre
//costul minim pana la nodul adiacent cu
//ruta care exclude nodul "nod" si costul muchiei
//dintre nodul adiacent si nodul initial gasit;
//stergerea nodului din ruta se va face cu ajutorul
//xor-ului pe biti, mai precis: ruta xor (2^nod).
cost_cu_ruta_nod[ruta][nod] = min(cost_cu_ruta_nod[ruta][nod], cost_cu_ruta_nod[ruta ^ (1 << nod)][lista_adiacenta[nod][i]] + costuri_muchii[lista_adiacenta[nod][i]][nod]);
}
}
}
}
}
int cost_minim = maxim;
//costul minim cerut va fi cea mai mica valoare a sumei dintre costul
//minim pana la un nod adiacent cu nodul 0 pe ruta compusa din toate
//nodurile grafului si costul muchiei dintre nodul adiacent si nodul 0
int n_muchii_ad = lista_adiacenta[0].size();
for (int i = 0; i < n_muchii_ad; i++){
cost_minim = min(cost_minim, cost_cu_ruta_nod[(1 << nr_noduri) - 1][lista_adiacenta[0][i]] + costuri_muchii[lista_adiacenta[0][i]][0]);
}
return cost_minim;
}
bool Graf::BFS_cuplaj_maxim(vector<int>& pereche_L, vector<int>& pereche_R, vector<int>& nivel){
//declaram o coada de noduri in care vom pune nodurile de procesat
queue<int> coada_noduri;
for (int l = 1; l <= nr_noduri; l++){
//la inceput, vom pune in coada nodurile din multimea L necuplate
//daca un nod din L este necuplat, ii schimbam de asemenea nivelul
//pentru a-l marca in curs de procesare
if (pereche_L[l] == 0){
nivel[l] = 0;
coada_noduri.push(l);
} else {
//altfel, nu procesam nodul (momentan; inca este posibil sa fie
//procesat)
nivel[l] = maxim;
}
}
//initializam nivelul unui nod "nul" ca fiind maxim
nivel[0] = maxim;
//cat timp avem noduri de procesat
while (!coada_noduri.empty()){
int nod_L = coada_noduri.front();
coada_noduri.pop();
//daca nodul trebuie procesat
if (nivel[nod_L] < nivel[0]){
//parcurgem toate nodurile din R care sunt adiacente cu el
int nr_muchii_adiacente = lista_adiacenta[nod_L].size();
for (int i = 0; i < nr_muchii_adiacente; i++){
int nod_R = lista_adiacenta[nod_L][i];
//daca gasim un nod cu nivelul neinitializat (de obicei un nod
//deja cuplat sau cel nul), il procesam: ii actualizam nivelul
//cu cel al nodului + 1 si il punem in coada
//daca am gasit nodul nul, nu se vor mai adauga noduri in
//coada pentru ca nivelul nu poate creste peste cel al
//nodului nul
if (nivel[pereche_R[nod_R]] == maxim){
nivel[pereche_R[nod_R]] = nivel[nod_L] + 1;
coada_noduri.push(pereche_R[nod_R]);
}
}
}
}
//daca am gasit nodul nul (adica daca am dat de noduri din R necuplate),
//returnam true, altfel false si deci algoritmul se termina
return nivel[0] != maxim;
}
bool Graf::DFS_cuplaj_maxim(int nod_L, vector<int>& pereche_L, vector<int>& pereche_R, vector<int>& nivel){
//daca nodul nu este cel nul, putem face DFS cu el, altfel returnam true
if (nod_L != 0){
//parcurgem nodurile din R adiacente cu nodul dat
int nr_muchii_adiacente = lista_adiacenta[nod_L].size();
for (int i = 0; i < nr_muchii_adiacente; i++){
int nod_R = lista_adiacenta[nod_L][i];
//daca am marcat nodul din L care este acum cuplat cu nodul din R
//ca fiind urmatorul de procesat, continuam DFS-ul
if (nivel[pereche_R[nod_R]] == nivel[nod_L] + 1){
//daca, terminand algoritmul de DFS, ajungem la nodul nul,
//actualizam cuplajele nodurilor pe care le-am intalnit pe
//parcursul algoritmului de DFS si returnam true
if (DFS_cuplaj_maxim(pereche_R[nod_R], pereche_L, pereche_R, nivel)){
pereche_R[nod_R] = nod_L;
pereche_L[nod_L] = nod_R;
return true;
}
}
}
//altfel, marcam ca nu am gasit niciun cuplaj pentru nod si returnam
//false
nivel[nod_L] = maxim;
return false;
}
return true;
}
int Graf::cuplaj_maxim_hopcroft_karp(int nr_noduri_R, vector<pair<int, int>>& cuplaje){
//declaram vectorii pereche_L si pereche_R care vor indica pentru fiecare
//nod din fiecare multime care este nodul cu care sunt cuplati, si un
//vector nivel folosit pentru DFS-ul algoritmului si pentru a marca
//diferite stari ale nodurilor din multimea L
vector<int> pereche_L(nr_noduri + 1, 0);
vector<int> pereche_R(nr_noduri_R + 1, 0);
vector<int> nivel(nr_noduri + 1, 0);
int cuplaj_maxim = 0;
//cautam cu ajutorul unui BFS noduri din multimes R necuplate care pot fi
//cuplate cu un nod din multimea L
while (BFS_cuplaj_maxim(pereche_L, pereche_R, nivel)){
for (int l = 1; l <= nr_noduri; l++){
//pentru nodurile necuplate din multimea L, incercam sa le gasim
//un cuplaj printr-un DFS
if (pereche_L[l] == 0){
if (DFS_cuplaj_maxim(l, pereche_L, pereche_R, nivel)){
//daca am gasit un astfel de cuplaj, incrementam numarul
//de cuplaje gasite
cuplaj_maxim++;
}
}
}
}
for (int l = 1; l <= nr_noduri; l++){
if (pereche_L[l] != 0){
cuplaje.push_back(make_pair(l, pereche_L[l]));
}
}
return cuplaj_maxim;
}
void BFS_helper(){
Graf g;
int nod_start = g.citire_orientat_bfs("bfs.in");
vector<int> nivel = g.BFS(nod_start);
int numar_noduri = nivel.size();
ofstream fo("bfs.out");
for (int i = 1; i < numar_noduri; i++){
fo << nivel[i] << " ";
}
fo.close();
}
void DFS_helper(){
Graf g;
g.citire_neorientat("dfs.in");
int nr_comp_conexe = g.componente_conexe();
ofstream fo("dfs.out");
fo << nr_comp_conexe;
fo.close();
}
void biconex_helper(){
Graf g;
g.citire_neorientat("biconex.in");
vector<vector<int>> comp_biconexe = g.componente_biconexe();
//afisam numarul de componente biconexe
int nr_comp = comp_biconexe.size();
ofstream fo("biconex.out");
fo << nr_comp << "\n";
//incepem sa procesam fiecare componenta pentru afisare
for (int i = 0; i < nr_comp; i++){
//ordonam crescator nodurile care alcatuiesc componenta
sort(comp_biconexe[i].begin(), comp_biconexe[i].end());
//dupa executarea functiei, in comp_biconexe avem muchiile
//componentelor; trebuie sa lasam doar nodurile in componenta
comp_biconexe[i].erase(unique(comp_biconexe[i].begin(), comp_biconexe[i].end()), comp_biconexe[i].end());
//afisam nodurile care alcatuiesc componenta
int n_noduri = comp_biconexe[i].size();
for (int j = 0; j < n_noduri; j++){
fo << comp_biconexe[i][j] << " ";
}
fo << "\n";
}
fo.close();
}
void CTC_helper(){
Graf g;
g.citire_orientat("ctc.in");
vector<vector<int>> comp_tare_conexe = g.componente_tare_conexe();
int nr_comp = comp_tare_conexe.size();
ofstream fo("ctc.out");
fo << nr_comp << "\n";
//incepem sa procesam fiecare componenta pentru afisare
for (int i = 0; i < nr_comp; i++){
//ordonam crescator nodurile care alcatuiesc componenta
sort(comp_tare_conexe[i].begin(), comp_tare_conexe[i].end());
//afisam nodurile care alcatuiesc componenta
int n_noduri = comp_tare_conexe[i].size();
for (int j = 0; j < n_noduri; j++){
fo << comp_tare_conexe[i][j] << " ";
}
fo << "\n";
}
fo.close();
}
void havel_hakimi_helper(){
Graf g;
//declaram un vector secv_grade, care va stoca gradele pe care trebuie
//sa le aiba fiecare nod
vector<int> secv_grade;
int n;
ifstream fi("havelhakimi.in");
fi >> n;
for (int i = 0; i < n; i++){
int grad;
fi >> grad;
secv_grade.push_back(grad);
}
g.havel_hakimi(secv_grade, n, "havelhakimi.out");
fi.close();
}
void sort_top_helper(){
Graf g;
g.citire_orientat("sortaret.in");
vector<int> sortare_top = g.sort_top();
int n = sortare_top.size();
ofstream fo("sortaret.out");
for (int i = 0; i < n; i++){
fo << sortare_top[i] << " ";
}
fo.close();
}
void critical_connections_helper(){
Graf g;
g.citire_neorientat("critconn.in");
vector<vector<int>> lista_muchii_critice = g.muchii_critice();
int nr_muchii_critice = lista_muchii_critice.size();
ofstream fo("critconn.out");
for (int i = 0; i < nr_muchii_critice; i++){
//afisam muchiile critice, vectori de exact 2 elemente (cele 2 noduri)
for (int j = 0; j < 2; j++){
fo << lista_muchii_critice[i][j] << " ";
}
fo << "\n";
}
fo.close();
}
void APM_helper(){
Graf g;
//declaram un vector de vectori "m_cost" in care vom pune muchiile cu
//costurile asociate si o variabila "cost_apm" in care vom stoca costul
//cerut al APM-ului
vector<vector<int>> m_cost = g.citire_neorientat_cost("apm.in");
int cost_apm = 0;
//declaram un vector de vectori "apm" in care stocam muchiile APM-ului
//cerut
vector<vector<int>> apm = g.apm_Kruskal(m_cost, cost_apm);
int nr_muchii_apm = apm.size();
ofstream fo("apm.out");
fo << cost_apm << "\n";
fo << nr_muchii_apm << "\n";
for (int i = 0; i < nr_muchii_apm; i++){
//fiecare muchie este stocata intr-un vector cu exact 2 elemente
fo << apm[i][0] << " " << apm[i][1] << "\n";
}
fo.close();
}
void paduri_disjuncte_helper(){
Graf g;
g.paduri_disjuncte("disjoint.in", "disjoint.out");
}
void dijkstra_helper(){
Graf g;
g.citire_orientat_cost("dijkstra.in");
vector<int> lista_distante = g.dijkstra();
int nr_distante = lista_distante.size();
ofstream fo("dijkstra.out");
for (int i = 2; i < nr_distante; i++){
fo << lista_distante[i] << " ";
}
fo.close();
}
void bellmanford_helper(){
Graf g;
g.citire_orientat_cost("bellmanford.in");
bool exista_ciclu = false;
vector<int> lista_distante = g.bellman_ford(exista_ciclu, "bellmanford.out");
ofstream fo("bellmanford.out");
//daca NU avem niciun ciclu negativ in graf, afisam distantele cerute
if (!exista_ciclu){
int nr_distante = lista_distante.size();
for (int i = 2; i < nr_distante; i++){
fo << lista_distante[i] << " ";
}
}
fo.close();
}
void edmonds_karp_helper(){
Graf g;
vector<unordered_set<int>> lista_arce = g.citire_retea("maxflow.in");
int flux_maxim = g.edmonds_karp(lista_arce);
ofstream fo("maxflow.out");
fo << flux_maxim;
fo.close();
}
void floyd_warshall_helper(){
Graf g;
vector<vector<int>> muchii_ponderate = g.citire_orientat_cost_matrice("royfloyd.in");
vector<vector<int>> distante_minime = g.floyd_warshall(muchii_ponderate);
ofstream fo("royfloyd.out");
int numar_noduri = distante_minime.size();
for (int i = 0; i < numar_noduri; i++){
for (int j = 0; j < numar_noduri; j++){
fo << distante_minime[i][j] << " ";
}
fo << "\n";
}
fo.close();
}
void diametru_arbore_helper(){
Graf g;
g.citire_arbore("darb.in");
int ultim = 0;
int diametru = 0;
//primul apel al BFS-ului va fi din nodul 1
g.BFS_diametru_arbore(1, ultim, diametru);
//al doilea apel al BFS-ului este din ultimul nod gasit in primul apel si
//va da diametrul cerut
g.BFS_diametru_arbore(ultim, ultim, diametru);
ofstream fo("darb.out");
fo << diametru;
fo.close();
}
void ciclu_eulerian_helper(){
Graf g;
g.citire_neorientat("ciclueuler.in");
vector<int> ciclu_eulerian;
g.ciclu_euler(ciclu_eulerian, "ciclueuler.out");
//daca vectorul cu ciclul dat este gol, inseamna ca nu l-am gasit (ca
//ne-am oprit la verificarea conditiei de ciclu); altfel, afisam ciclul
if (!ciclu_eulerian.empty()){
ofstream fo("ciclueuler.out");
int lungime_ciclu = ciclu_eulerian.size();
for (int i = 0; i < lungime_ciclu - 1; i++){
fo << ciclu_eulerian[i] << " ";
}
fo.close();
}
}
void ciclu_hamiltonian_helper(){
Graf g;
vector<vector<int>> costuri_muchii = g.citire_orientat_cost_hamilton("hamilton.in");
int cost_minim = g.ciclu_hamilton(costuri_muchii);
ofstream fo("hamilton.out");
//daca costul minim este egal cu valoarea maxima a unui cost, inseamna ca
//valoarea lui initiala nu s-a modificat, deci ca nu am gasit ciclul
//hamiltonian cerut; Altfel, afisam costul minim asociat cu acesta
if (cost_minim == maxim){
fo << "Nu exista solutie";
} else {
fo << cost_minim;
}
fo.close();
}
void cuplaj_maxim_helper(){
Graf g;
int nr_noduri_R = g.citire_bipartit("cuplaj.in");
vector<pair<int, int>> cuplaje;
int cuplaj_maxim = g.cuplaj_maxim_hopcroft_karp(nr_noduri_R, cuplaje);
ofstream fo("cuplaj.out");
fo << cuplaj_maxim << "\n";
int nr_cuplaje = cuplaje.size();
for (int i = 0; i < nr_cuplaje; i++){
fo << cuplaje[i].first << " " << cuplaje[i].second << "\n";
}
fo.close();
}
int main()
{
//1. algoritmul BFS
//BFS_helper();
//2. componente conexe (algoritmul DFS)
//DFS_helper();
//3. componente biconexe
//biconex_helper();
//4. componente tare conexe (algoritmul lui Tarjan)
//CTC_helper();
//5. algoritmul Havel-Hakimi
//havel_hakimi_helper();
//6. sortare topologica
//sort_top_helper();
//7. muchii critice
//critical_connections_helper();
//8. arbore partial de cost minim (APM) - algoritmul lui Kruskal
//APM_helper();
//9. paduri de multimi disjuncte
//paduri_disjuncte_helper();
//10. algoritmul lui Dijkstra
//dijkstra_helper();
//11. algoritmul Bellman-Ford
//bellmanford_helper();
//12. flux maxim (algoritmul Edmonds-Karp)
//edmonds_karp_helper();
//13. algoritmul Floyd-Warshall
//floyd_warshall_helper();
//14. diametrul unui arbore
//diametru_arbore_helper();
//15. ciclu eulerian
ciclu_eulerian_helper();
//16. ciclu hamiltonian
//ciclu_hamiltonian_helper();
//17. cuplaj maxim (algoritmul Hopcroft-Karp)
//cuplaj_maxim_helper();
return 0;
}