Cod sursa(job #2818869)

Utilizator Pop_MariaPop Maria Pop_Maria Data 17 decembrie 2021 05:21:53
Problema Componente biconexe Scor 90
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 34.39 kb
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <unordered_map>
#define INF 0x3f3f3f3f

using namespace std;

ifstream fin("biconex.in");
ofstream fout("biconex.out");

class Graf
{
    int numar_noduri, numar_muchii;
    vector <vector <int>> lista_adiacenta;

    /// vector folosit pentru problemele "Dijkstra" si "Bellman-Ford"
    vector <vector <pair<int, int>>> lista_adiacenta2;

    /// date folosite pentru problema "APM"
    struct componente_muchie
    {
        int capat_stang, capat_drept, cost;
    };
    vector <componente_muchie> muchii;

    /// date folosite pentru problema "Critical Connections"
    vector <vector <int>> connections;
    vector<vector<int>> x;

public:

    /// functii de citire pentru grafuri orientate si grafuri neorientate
    void citire_graf_orientat(int, int);
    void citire_graf_neorientat(int, int);

    /// functia folosita pentru problema BFS
    void bfs_initializare();
    void bfs(int);

    /// functiile folosite pentru problema DFS
    void dfs_initializare();
    int numar_componente_conexe();
    void dfs(vector <int> &, int nod);

    /// functiile folosite pentru problema CTC
    void ctc_initializare();
    void parcurgere(vector <vector <int>> &);
    void dfs1(int, unordered_map <int, bool>&, vector <int> &);
    void dfs2(int, unordered_map <int, bool>&, vector <int> &, int, vector <vector <int>> &, vector <vector <int>> &);

    /// functia folosita pentru Havel-Hakimi
    void havel_hakimi();

    /// functiile folosite pentru problema sortaret
    void sortare_topologica_initializare();
    void sortare_topologica();
    void functie(int nod, int vizitarest[], stack <int> &stiva);

    /// functiile folosite pentru problema biconex
    void biconex_initializare();
    void dfs_biconex1();
    void dfs_biconex2(int, int, int &, stack <int> &, vector <vector <int>>&, vector <int> &, vector <int> &, vector<int> &);

    /// functiile folosite pentru problema "Flux maxim";
    void citire_flux();
    int max_flow(vector <vector <pair <int, int>>>, int, int, int);
    int bfs_maxflow(int, int, vector <vector <int>>&, vector<int>&, vector <vector <int>>&, vector <int>&, vector <vector<int>>&);

    /// functiile folosite pentru problema "Floyd-Washall/Roy-Floyd"
    void citire_roy_floyd();
    vector <vector <int>> roy_floyd(vector <vector <int>>matrice_ponderi, int cost)
    {
        // complexitate algoritm: O(n^3)
        vector <vector <int>> distante = matrice_ponderi;

        for(int i = 1; i <= numar_noduri; i++)
            for(int j = 1; j <= numar_noduri; j++)
                if(i != j && !matrice_ponderi[i][j])
                    distante[i][j] = cost;      // adaugam in matricea drumurilor minime costurile muchiilor pentru nodurile adiacente

        for(int i = 1; i <= numar_noduri; i++)
            for(int j = 1; j <= numar_noduri; j++)      // incercam sa gasim pentru orice 2 noduri j si k, un nod i pe care daca-l parcurgem, obtinem o distanta mai mica
                for(int k = 1; k <= numar_noduri; k++)  // intre nodurile j si k
                    if(j != k && distante[j][k] > distante[j][i] + distante[i][k])  // verificam daca se indeplineste conditiasi daca j si k sunt acelasi nod
                        distante[j][k] = distante[j][i] + distante[i][k];

        return distante;
    }

    /// functiile folosite pentru problema "Diametrul unui arbore"
    void citire_darb();
    void bfs_darb_init();
    void bfs_darb(int, int &, int &);

    /// functiile folosite pentru problema APM
    void citire();
    void apm();
    int gasire(int, int[]);
    void unire(int, int, int[], int[]);

    /// functiile folosite pentru problema "Paduri de multimi disjuncte"
    void disjoint();
    int gasire_multime(int, int[]);
    void operatie_tip_1(int, int, int[], int[]);

    /// de aici incep functiile pentru problema "critical connections"
    void citire_leetcode();
    void functie1(vector<int> adiacenta[], vector<vector<int>>& connections)
    {
        for(int i = 0; i < connections.size(); i++)
        {
            adiacenta[connections[i][0]].push_back(connections[i][1]);
            adiacenta[connections[i][1]].push_back(connections[i][0]);
        }
    }

    void dfs_leetcode(vector <int> adiacenta[], int v1[], int v2[], bool vizitare[], int s, int nod, int &numar1)
    {
        vizitare[s] = true;
        v1[s] = v2[s] = ++numar1;

        for(int &i: adiacenta[s])
        {
            if(!vizitare[i])
            {
                dfs_leetcode(adiacenta, v1, v2, vizitare, i, s, numar1);
                v2[s] = min(v2[s], v2[i]);

                if(v2[i] > v1[s])
                    x.push_back({s, i});
            }
            else if(i != nod)
                v2[s] = min(v2[s], v1[i]);
        }
    }
    vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections)
    {

        vector <int> adiacenta[n];
        functie1(adiacenta, connections);

        int v1[n];
        fill(v1, v1 + n, 0);

        int v2[n];
        fill(v2, v2 + n, 0);

        bool vizitare[n];
        fill(vizitare, vizitare + n, false);

        int numar1 = 0;
        dfs_leetcode(adiacenta, v1, v2, vizitare, 0, -1, numar1);
        return x;
    }
    /// aici se termina functiile pentru problema "critical connections"

    /// pentru problema "Dijkstra"
    void dijkstra1();

    vector <int> dijkstra2(const int a)
    {
        int b;
        vector <int> lungime_drum(numar_noduri + 1, INF);   // aici retinem lungimea drumurilor cautate
        vector <bool> pq(numar_noduri + 1, 0);  // pentru a marca nodurile vizitate
        // priority queue folosit pentru a retine nodul cel mai apropiat
        priority_queue <pair <int, int>, vector <pair <int, int>>, greater<pair<int, int>>> coada;

        // lungimea drumul de la nodul de incepere la el insusi este 0
        lungime_drum[a] = 0;
        coada.push(make_pair(0, a));

        // parcurgem elementele priority queue-ului
        while(!coada.empty())
        {
            b = coada.top().second; // cel mai mic nod
            coada.pop();

            // daca nu a fost vizitat
            if(pq[b] == 0)
            {
                pq[b] = 1;  // il marcam ca fiind vizitat
                // ii verificam vecinii
                for(int i = 1; i < lista_adiacenta2[b].size(); i++)
                    // daca distanta pana la nod e mai mare decat lungimea calculata pana la momentul acesta, o modificam
                    if(lungime_drum[lista_adiacenta2[b][i].first] > lungime_drum[b] + lista_adiacenta2[b][i].second)
                {
                    lungime_drum[lista_adiacenta2[b][i].first] = lungime_drum[b] + lista_adiacenta2[b][i].second;
                    coada.push(make_pair(lungime_drum[lista_adiacenta2[b][i].first], lista_adiacenta2[b][i].first));
                }
            }
        }

        return lungime_drum;

    }

    /// pentru problema "Bellman-Ford"
    void bellman_ford1();
    vector <int> bellman_ford2(const int a)
    {
            int ok = 1, b;
            queue <int> coada;
            vector <bool> m(numar_noduri + 1, false);
            vector <int> v(numar_noduri + 1, 0);
            vector <int> distanta(numar_noduri + 1, INF);

            // marcam nodul de la care incepem
            m[a] = 1;
            distanta[a] = 0;
            coada.push(a);

            while(ok && !coada.empty())
            {
                b = coada.front();
                m[b] = 0;
                coada.pop();

                // parcurgem lista de adiacenta a nodului curent
                for(int i = 1; i < lista_adiacenta2[b].size(); i++)
                    // daca avem un cost mai mare decat costul pe care l-am forma cu muchia actuala, il modificam
                    if(distanta[b] + lista_adiacenta2[b][i].second < distanta[lista_adiacenta2[b][i].first])
                {
                    v[lista_adiacenta2[b][i].first]++;
                    distanta[lista_adiacenta2[b][i].first] = distanta[b] + lista_adiacenta2[b][i].second;

                    // daca al doilea nod nu este in queue, il adaugam
                    if(m[lista_adiacenta2[b][i].first] == 0)
                    {
                        coada.push(lista_adiacenta2[b][i].first);
                        m[lista_adiacenta2[b][i].first] = 1;
                    }

                    // daca un nod a fost relaxat de un numar mai mare de ori decat numarul de noduri, inseamna ca exista un ciclu
                    if(v[lista_adiacenta2[b][i].first] >= numar_noduri)
                        ok = 0;

                }
            }

    if(ok == 0)
        distanta.clear();

    return distanta;
    }

};

/// functia de citire pentru grafuri orientate
void Graf :: citire_graf_orientat(int numar_noduri, int numar_muchii)
{
    int capat_stang, capat_drept;
    lista_adiacenta.resize(numar_noduri + 1);

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> capat_stang >> capat_drept;
        lista_adiacenta[capat_stang].push_back(capat_drept);
    }
}

/// functia de citire pentru grafuri neorientate
void Graf :: citire_graf_neorientat(int numar_noduri, int numar_muchii)
{
    int capat_stang, capat_drept;
    lista_adiacenta.resize(numar_noduri + 1);

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> capat_stang >> capat_drept;
        lista_adiacenta[capat_stang].push_back(capat_drept);
        lista_adiacenta[capat_drept].push_back(capat_stang);
    }
}

/// functii pentru problema "BFS"
void Graf :: bfs_initializare()
{
    int nod_start;

    fin >> numar_noduri >> numar_muchii >> nod_start;

    citire_graf_orientat(numar_noduri, numar_muchii);

    bfs(nod_start);

    lista_adiacenta.clear();
}

/// algoritmul bfs - parcurgerea in latime a unui graf
void Graf :: bfs(int nod_start)
{
    int nod_curent;
    queue <int> coada;
    int vizitare[numar_noduri + 1] = {};

    coada.push(nod_start);  // adaugam in coada nodul de la care se incepe parcurgerea
    vizitare[nod_start] = 1;    // marcam nodul de start ca fiind vizitat

    int cost_nod[numar_noduri + 1] = {};   // pentru retinerea numarului minim de arce ce trebuie parcurse
    cost_nod[nod_start] = 0;

    while(coada.size() > 0)
    {
        nod_curent = coada.front();

        for(int i = 0; i < lista_adiacenta[nod_curent].size(); i++)
            if(!vizitare[lista_adiacenta[nod_curent][i]])   // daca un nod adiacent cu nodul curent nu este vizitat se executa codul de mai jos
        {
            coada.push(lista_adiacenta[nod_curent][i]); // adaugam in queue nodurile nevizitate
            cost_nod[lista_adiacenta[nod_curent][i]] = cost_nod[nod_curent] + 1;
            vizitare[lista_adiacenta[nod_curent][i]] = 1;   // le marcam ca fiind vizitate
        }
        coada.pop();    // scoatem din coada nodul curent
    }

    for(int i = 1; i <= numar_noduri; i++)
        if(vizitare[i])
            fout << cost_nod[i] << " "; // afisam numarul minim de arce ce trebuie parcurse de la nodul de start la nodul i
        else
            fout << "-1 ";  // afisam -1 pentru nodurile la care nu se poate ajunge din nodul de start

}

/// functii folosite pentru problema "DFS"
void Graf :: dfs_initializare()
{
    fin >> numar_noduri >> numar_muchii;

    citire_graf_neorientat(numar_noduri, numar_muchii);

    fout << numar_componente_conexe();

    lista_adiacenta.clear();
}

/// functia pentru numararea componentelor conexe
int Graf :: numar_componente_conexe()
{
    int numar_componente = 0;
    vector <int> vizitare;

    for(int i = 1; i <= numar_noduri + 1; i++)
        vizitare.push_back(0);      // vector folosit pentru a sti daca un nod a fost sau nu vizitat

    for(int i = 1; i <= numar_noduri; i++)
        if(!vizitare[i])    // daca nodul a fost vizitat, inseamna ca apartine unei componente conexe numarate deja
    {
        dfs(vizitare, i);   // numarul de componente conexe este egal cu numarul de apeluri ale functiei dfs din interiorul functiei numar_componente_conexe
        numar_componente++;
    }
    return numar_componente;
}

/// algoritmul dfs - parcurgerea in adancime a grafului
void Graf :: dfs(vector <int> &vizitare, int nod)
{
    vizitare[nod] = 1;  // marcam nodul curent ca fiind vizitat

    for(int i = 0; i < lista_adiacenta[nod].size(); i++)
        if(!vizitare[lista_adiacenta[nod][i]])
            dfs(vizitare, lista_adiacenta[nod][i]);    // apelam in mod recursiv functia dfs pentru a parcurge toate nodurile dintr-o componenta conexa

}

/// functii folosite pentru problema "CTC"
void Graf :: ctc_initializare()
{
    int capat_stang, capat_drept;
    vector <vector <int>> lista_adiacenta1;

    fin >> numar_noduri >> numar_muchii;

    lista_adiacenta.resize(numar_noduri + 1);
    lista_adiacenta1.resize(numar_noduri + 1);

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> capat_stang >> capat_drept;
        lista_adiacenta[capat_stang].push_back(capat_drept);    // listele de adiacenta pentru graful dat
        lista_adiacenta1[capat_drept].push_back(capat_stang);   // listele de adiacenta pentru graful transpus
    }

    parcurgere(lista_adiacenta1);

    lista_adiacenta.clear();
    lista_adiacenta1.clear();
}


/// functie pentru problema "CTC" pentru rezolvarea careia s-a folosit algoritmul lui Kosaraju
void Graf :: parcurgere(vector <vector <int>> &lista_adiacenta1)
{
    int numar_componente = 0;
    vector <int> st;    // vectorul in care vom stoca nodurile
    vector < vector <int>> componenta;
    unordered_map <int, bool> vizitare1, vizitare2;

    for(int i = 1; i <= numar_noduri; i++)
        if(!vizitare1[i])
            dfs1(i, vizitare1, st);     // realizam o parcurgere in adancime a grafului dat

    for(int i = st.size() - 1; i >= 0; i--)
        if(!vizitare2[st[i]])   // pornind de la ultimul nod adaugat in vector, realizam parcurgerea in adancime a grafului transpus
    {
        dfs2(st[i], vizitare2, st, numar_componente, componenta, lista_adiacenta1);
        numar_componente++;     // numarul de componente conexe tare este egal cu numarul de apeluri ale functiei de parcurgere in adancime a grafului transpus
    }

    // afisarea rezultatului
    fout << numar_componente << '\n';

    for(int i = 0; i < numar_componente; i++)
    {
        for(int j = 0; j < componenta[i].size(); j++)
            fout << componenta[i][j] << " ";

        fout << '\n';
    }
}

/// primul algoritm DFS pentru problema "CTC"
void Graf :: dfs1(int nod, unordered_map <int, bool> &vizitare1, vector <int> &st)
{
    vizitare1[nod] = true;  // marcam nodul curent ca fiind vizitat

    for(int i = 0; i < lista_adiacenta[nod].size(); i++)
        if(!vizitare1[lista_adiacenta[nod][i]])
            dfs1(lista_adiacenta[nod][i], vizitare1, st);   // apelam in mod recursiv primul algoritm DFS pentru graful dat

    st.push_back(nod);      // introducem in vector nodurile in ordinea inversa fata de cea in care au fost vizitate
}

/// al doilea algoritm DFS pentru problema "CTC"
void Graf :: dfs2(int nod, unordered_map <int, bool> &vizitare2, vector <int> &st, int numar_componente, vector <vector <int>> &componenta, vector <vector <int>> &lista_adiacenta1)
{
    vizitare2[nod] = true;
    componenta.resize(numar_componente + 1);
    componenta[numar_componente].push_back(nod);    // adaugam in fiecare componenta tare conexa nodurile corespunzatoare

    for(int i = 0; i < lista_adiacenta1[nod].size(); i++)
        if(!vizitare2[lista_adiacenta1[nod][i]])
            dfs2(lista_adiacenta1[nod][i], vizitare2, st, numar_componente, componenta, lista_adiacenta1);    // apelam in mod recursiv al doilea algoritm DFS pentru graful transpus
}

/// Havel-Hakimi
void Graf :: havel_hakimi()
{
    int ok = 1, nod, numar_havel;
    vector <int> grade;

    fin >> numar_havel;

    // citim lista de numere pe care le introducem intr-o coada
    while(numar_havel)
    {
        fin >> nod;
        grade.push_back(nod);
        numar_havel--;
    }

    while(true)
    {
        sort(grade.begin(), grade.end(), greater<>());  // sortam lista de grade pentru ca gradul cel mai mare sa fie primul

        // daca cel mai mare grad din lista este 0, atunci putem construi un graf
        if(!grade[0])
            break;

        int x = grade[0];
        grade.erase(grade.begin()); // conform algoritmului Havel-Hakimi, eliminam primul element din lista

        // daca cel mai mare grad al unui nod este mai mare decat numarul de elemente ramase in lista, adica de noduri, inseamna ca nu putem construi un graf
        // pentru ca nodul cu gradul cel mai mare nu ar avea un numar suficient de noduri cu care sa se conecteze
        if(grade.size() < x)
        {
            ok = 0;
            break;
        }

        for(int i = 0; i < x; i++)
        {
            grade[i]--;     // conform algoritmului, "eliminam" muchiile incidente cu nodul eliminat scazand cu 1 din gradele urmatoarelor x noduri

            if(grade[i] < 0)    // daca intalnim un nod cu un grad negativ, nu putem construi un graf
            {
                ok = 0;
                break;
            }
        }

        if(ok == 0)     // conditie pusa pentru iesirea din while pentru cazul in care ok ar fi devenit 0 in cadrul structurii repetitive for
            break;
    }

    if(ok)      // daca ok nu a devenit 0 in timpul structurii repetitive while, atunci inseamna ca putem construi un graf
        fout << "Da";
    else
        fout << "Nu";

}

/// functii folosite pentru problema "sortaret"
void Graf :: sortare_topologica_initializare()
{
    fin >> numar_noduri >> numar_muchii;

    lista_adiacenta.resize(numar_noduri + 1);

    citire_graf_orientat(numar_noduri, numar_muchii);

    sortare_topologica();

    lista_adiacenta.clear();
}


/// pentru sortarea topologica
void Graf :: sortare_topologica()
{
    stack <int> stiva;  // stiva in care stocam nodurile sortate invers topologic
    int vizitare[numar_noduri + 1] = {};

    for(int i = 1; i <= numar_noduri; i++)
        if(!vizitare[i])
            functie(i, vizitare, stiva);    // folosim algoritmul DFS pentru fiecare componenta conexa a grafului

    // afisam nodurile sortate topologic
    while(!stiva.empty())
    {
        fout << stiva.top() << " ";
        stiva.pop();
    }
}

/// apartine problemei "sortaret" - algoritm DFS
void Graf :: functie(int nod, int vizitare[], stack <int> &stiva)
{
    vizitare[nod] = 1;  // marcam nodul curent ca fiind vizitat

    for(int i = 0; i < lista_adiacenta[nod].size(); i++)
        if(!vizitare[lista_adiacenta[nod][i]])
            functie(lista_adiacenta[nod][i], vizitare, stiva);  // apelam in mod recursiv functia pentru a parcurge toate nodurile nevizitate
                                                                // dintr-o componenta conexa
    stiva.push(nod);    // dupa ce am vizitat toate nodurile care depind de nodul curent, adaugam nodul curent in stiva
}

/// functii folosite pentru problema "Biconex"
void Graf :: biconex_initializare()
{
    fin >> numar_noduri >> numar_muchii;

    lista_adiacenta.resize(numar_noduri + 1);

    citire_graf_neorientat(numar_noduri, numar_muchii);

    dfs_biconex1();

    lista_adiacenta.clear();
}

/// pentru problema biconex
void Graf :: dfs_biconex1()
{
    int numar = 0;      // numarul de componente biconexe
    stack <int> st;
    vector <int> nivel1(numar_noduri + 1);
    vector <int> nivel2(numar_noduri + 1);
    vector <int> nivel3(numar_noduri + 1);
    vector <vector <int>> componente_biconexe(numar_noduri + 1);

    dfs_biconex2(1, 0, numar, st, componente_biconexe, nivel1, nivel2, nivel3);

    // afisarea rezultatului
    fout << numar << '\n';

    for(int i = 0; i < numar; i++)
    {
        for(int j = 0; j < componente_biconexe[i].size(); j++)
            fout << componente_biconexe[i][j] << " ";
        fout << '\n';
    }
}

void Graf :: dfs_biconex2(int x, int parinte, int &numar, stack <int> &st, vector <vector <int>>& componente_biconexe, vector <int> &nivel1, vector <int> &nivel2, vector <int> &nivel3)
{
        st.push(x);
        nivel2[x] = nivel2[parinte] + 1;
        nivel1[x] = nivel2[x];
        nivel3[x] = 1;

        for(int i = 0; i < lista_adiacenta[x].size(); i++)
        {
            int y = lista_adiacenta[x][i];

            if(parinte != y)
                if(nivel3[y] == 1)
            {
                if(nivel1[x] > nivel2[y])
                    nivel1[x] = nivel1[y];
            }
            else
            {
                dfs_biconex2(lista_adiacenta[x][i], x, numar, st, componente_biconexe, nivel1, nivel2, nivel3);
                if(nivel1[x] > nivel1[y])
                    nivel1[x] = nivel1[lista_adiacenta[x][i]];
                if(nivel2[x] <= nivel1[y])
                {
                    while(st.top() != y)
                    {
                        componente_biconexe[numar].push_back(st.top());
                        st.pop();
                    }

                    componente_biconexe[numar].push_back(y);
                    componente_biconexe[numar].push_back(x);
                    st.pop();
                    numar++;
                }
            }
        }
}


/// functii folosite pentru problema "Flux maxim"
int Graf :: bfs_maxflow(int sursa, int destinatie, vector <vector <int>>& capacitati, vector <int> &tata, vector <vector <int>>&flux, vector <int> &vizitat, vector <vector<int>>&rezidual)
{
    int a;
    queue <int> coada;
    coada.push(sursa);

    tata.assign(capacitati.size(), 0);
    tata[sursa] = -1;

    vizitat.clear();
    vizitat.resize(capacitati.size(), 0);

    vizitat[sursa] = 1;

    while(!tata[destinatie] && !coada.empty())
    {
        a = coada.front();
        coada.pop();

        for(int i : rezidual[a])
            if(capacitati[a][i] > flux[a][i] && !vizitat[i])
        {
            tata[i] = a;
            vizitat[i] = 1;
            coada.push(i);
        }

    }
    return tata[destinatie];

}

int Graf :: max_flow(vector <vector <pair <int, int>>> lista_adiacenta, int sursa, int destinatie, int capacitate_maxima)
{
    int maxim = 0, flux_drum, a;
    vector <int> vizitat(numar_noduri + 1, 0);
    vector <int> tata(numar_noduri + 1, 0);
    vector <vector <int>> rezidual(numar_noduri + 1);
    vector <vector <int>> capacitati(numar_noduri + 1, vector <int>(numar_noduri + 1, 0));
    vector <vector <int>> flux(numar_noduri + 1, vector <int>(numar_noduri + 1, 0));

    for(int i = 0; i < numar_noduri + 1; i++)
        for(int j = 0; j < lista_adiacenta[i].size(); j++)
    {
        capacitati[i][lista_adiacenta[i][j].first] = lista_adiacenta[i][j].second;
        rezidual[i].push_back(lista_adiacenta[i][j].first);
        rezidual[lista_adiacenta[i][j].first].push_back(i);
    }

    while(bfs_maxflow(sursa, destinatie, capacitati, tata, flux, vizitat, rezidual))
    {
        for(int i : rezidual[destinatie])
            if(flux[i][destinatie] < capacitati[i][destinatie] && vizitat[i])
        {
            flux_drum = capacitate_maxima;
            tata[destinatie] = i;

            for(int j = destinatie; j != sursa; j = tata[j])
            {
                a = tata[j];

                if(flux_drum > capacitati[a][j] - flux[a][j])
                    flux_drum = capacitati[a][j] - flux[a][j];
            }
            if(flux_drum)
            {
                for(int j = destinatie; j != sursa; j = tata[j])
            {
                a = tata[j];
                flux[a][j] += flux_drum;
                flux[j][a] -= flux_drum;
            }

            maxim += flux_drum;
            }

        }
    }

    return maxim;
}


void Graf :: citire_flux()
{
    int nod1, nod2, capacitate;

    fin >> numar_noduri >> numar_muchii;

    vector <vector <pair <int, int>>> lista_adiacenta;
    lista_adiacenta.resize(numar_noduri + 1);

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> nod1 >> nod2 >> capacitate;
        lista_adiacenta[nod1].push_back(make_pair(nod2, capacitate));
    }

    fout << max_flow(lista_adiacenta, 1, numar_noduri, 110001);
}


/// functie folosita pentru problema "Roy-Floyd"
void Graf :: citire_roy_floyd()
{
    // citire date
    fin >> numar_noduri;

    vector <vector <int>> matrice_ponderi;
    matrice_ponderi.resize(numar_noduri + 1);

    for(int i = 1; i <= numar_noduri; i++)
    {
        matrice_ponderi[i].resize(numar_noduri + 1);
        for(int j = 1; j <= numar_noduri; j++)
            fin >> matrice_ponderi[i][j];
    }

    // in matricea distante vom retine matricea drumurilor minime
    vector <vector <int>> distante = roy_floyd(matrice_ponderi, 1001);

    // afisare rezultat
    for(int i = 1; i <= numar_noduri; i++)
    {
        for(int j = 1; j <= numar_noduri; j++)
            fout << distante[i][j] << " ";
        fout << '\n';
    }
}


void Graf :: bellman_ford1()
{
    int nod1, nod2, cost;
    vector <pair <int, int>> x(1, make_pair(-1, -1));

    fin >> numar_noduri >> numar_muchii;

    for(int i = 0; i <= numar_noduri + 1; i++)
        lista_adiacenta2.push_back(x);

    // citire date
    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> nod1 >> nod2 >> cost;
        lista_adiacenta2[nod1].push_back(make_pair(nod2, cost));
    }

    // retinem in vectorul distanta rezultatul
    vector <int> distanta = bellman_ford2(1);

    // afisarea rezultatului
    if(!distanta.size())
        fout << "Ciclu negativ!";
    else for(int i = 2; i <= numar_noduri; i++)
        fout << distanta[i] << " ";
}

/// functia de citire pentru problema "critical connections"
void Graf :: citire_leetcode()
{
    int n, x, y;
    fin >> n;

    for(int i = 0; i < n; i++)
    {
        fin >> x >> y;
        connections.push_back({x, y});
    }

    vector <vector <int>> rezultat = criticalConnections(n, connections);

    for(int i = 0; i < rezultat.size(); i++)
    {
        for(int j = 0; j < rezultat[i].size(); j++)
            fout << rezultat[i][j] << " ";
        fout << '\n';
    }
}

/// pentru problema Dijkstra
void Graf :: dijkstra1()
{
    int nod1, nod2, lungime_arc;
    vector <pair <int, int>> lista(1, make_pair(-1, -1));

    fin >> numar_noduri >> numar_muchii;

    for(int i = 0; i <= numar_noduri + 1; i++)
        lista_adiacenta2.push_back(lista);

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> nod1 >> nod2 >> lungime_arc;
        lista_adiacenta2[nod1].push_back(make_pair(nod2, lungime_arc));
    }

    // in acest vector am stocat rezultatul
    vector <int> lungime_drum = dijkstra2(1);

    // afisarea rezultatului
    for(int i = 2; i <= numar_noduri; i++)
        if(lungime_drum[i] != INF)
            fout << lungime_drum[i] << " ";
        else
            fout << 0 << " ";
}

/// functia de citire pentru problema APM
void Graf :: citire()
{
    int capat_stang, capat_drept, cost;

    fin >> numar_noduri >> numar_muchii;

    for(int i = 0; i < numar_muchii; i++)
    {
        fin >> capat_stang >> capat_drept >> cost;
        muchii.push_back({capat_stang, capat_drept, cost});
    }
}

int Graf :: gasire(int a, int noduri1[])
{
    int aux = a, aux2;

    while(noduri1[aux] != aux)
    {
        aux = noduri1[aux];
    }

    while(a != aux)
    {
        aux2 = noduri1[a];
        noduri1[a] = aux;
        a = aux2;
    }

    return a;
}

void Graf :: unire(int a, int b, int noduri1[], int noduri2[])
{
    a = gasire(a, noduri1);
    b = gasire(b, noduri1);

    if(noduri2[a] < noduri2[b])
        noduri1[a] = b;
    else
    {
        noduri1[b] = a;
        if(noduri2[a] == noduri2[b]) noduri2[a]++;
    }
}

void Graf :: apm()
{
    int numar_muchii_selectate = 0;
    int cost_minim = 0;
    int noduri1[numar_noduri + 1], noduri2[numar_noduri + 1];

    for(int i = 1; i <= numar_noduri; i++)
        noduri1[i] = i;

    // sortam muchiile dupa cost
    sort(muchii.begin(), muchii.end(), [](const componente_muchie &x, const componente_muchie &y) {return x.cost < y.cost;});

    vector <pair <int, int>> rezultat;

    for(const componente_muchie& x : muchii)
    {
        if(numar_muchii_selectate == numar_noduri - 1)
            break;

        if(gasire(x.capat_stang, noduri1) == gasire(x.capat_drept, noduri1))
            continue;

        numar_muchii_selectate++;
        cost_minim += x.cost;

        unire(x.capat_stang, x.capat_drept, noduri1, noduri2);
        rezultat.push_back({x.capat_stang, x.capat_drept});
    }

    fout << cost_minim << '\n';
    fout << rezultat.size() << '\n';

    for(auto x : rezultat)
        fout << x.second << " " << x.first << '\n';
}

int Graf :: gasire_multime(int a, int parinte[])
{
    while(a != parinte[a])
        a = parinte[a];     // cautam radacina arborelui din care face parte nodul a

    return a;
}

void Graf :: operatie_tip_1(int a, int b, int dimensiune[], int parinte[])
{
    a = gasire_multime(a, parinte);
    b = gasire_multime(b, parinte);

    // unim arborii in functie de rang
    if(dimensiune[b] <= dimensiune[a])
    {
        dimensiune[a] += dimensiune[b];
        parinte[b] = a;
    }
    else
    {
        dimensiune[b] += dimensiune[a];
        parinte[a] = b;
    }
}

/// functia pentru "Paduri de multimi disjuncte"
void Graf :: disjoint()
{
    int numar_multimi, numar_operatii, numar1, numar2, tip_operatie;
    int parinte[1001], dimensiune[1001];

    fin >> numar_multimi >> numar_operatii;

    for(int i = 0; i < numar_multimi; i++)
    {
        dimensiune[i] = 1;  // rangul fiecarui nod este 1 initial
        parinte[i] = i; // tinem minte tatii
    }

    // executam fiecare operatie in parte pe masura ce citim datele
    for(int i = 0; i < numar_operatii; i++)
    {
        fin >> tip_operatie >> numar1 >> numar2;

        if(tip_operatie == 1)
            // unim radacinile arborilor corespunzatori multimilor celor 2 noduri
            operatie_tip_1(numar1, numar2, dimensiune, parinte);

            // verific daca radacina arborilor in care se afla cele 2 noduri este aceeasi
        else if(gasire_multime(numar1, parinte) == gasire_multime(numar2, parinte))
                    fout << "DA" << '\n';
        else fout << "NU" << '\n';
    }
}

/// pentru problema "Diametrul unui arbore"
void Graf :: citire_darb()
{
    int capat_stang, capat_drept;

    fin >> numar_noduri;

    for(int i = 0; i <= numar_noduri; i++)
        lista_adiacenta[i].push_back(-1);

    for(int i = 0; i < numar_noduri - 1; i++)
    {
        fin >> capat_stang >> capat_drept;
        lista_adiacenta[capat_stang].push_back(capat_drept);
        lista_adiacenta[capat_drept].push_back(capat_stang);
    }
}

void Graf :: bfs_darb_init()
{
    int distanta, nod1, nod2;

    // realizam 2 parcurgeri in latime pornind prima parcurgere de la primul nod si continuand cu a doua din ultimul nod in care am ajuns
    // cea mai indepartata frunza din prima parcurgere reprezinta un capat al lantului
    // urmand sa ii gasim celalalt capat in ultimul nod in care ajungem cu cea de a doua parcurgere
    bfs_darb(1, nod1, distanta);
    bfs_darb(nod1, nod2, distanta);

    fout << distanta;
}

/// algoritm BFS pentru gasirea diametrului unui arbore
void Graf :: bfs_darb(int s, int &u, int &d)
{
    int a;
    int vizitat[numar_noduri + 1] = {};
    int dist[numar_noduri + 1] = {};
    queue <int> coada;

    d = 0;      // in d retinem distanta maxima pentru a putea gasi cel mai indepartat nod
    dist[s] = 1;    // numaram nodurile distanta
    vizitat[s] = 1;     // marcam nodul de start ca fiind vizitat
    coada.push(s);  // adaugam in coada nodul de la care se porneste parcurgerea

    while(!coada.empty())
    {
        a = coada.front();
        coada.pop();

        for(int i = 1; i < lista_adiacenta[a].size(); i++)
            if(vizitat[lista_adiacenta[a][i]] == 0) // daca un nod adiacent cu nodul curent nu este vizitat, il adaugam in coada
        {
            vizitat[lista_adiacenta[a][i]] = 1;     // il marcam ca fiind vizitat
            coada.push(lista_adiacenta[a][i]);      // il adaugam in coada
            dist[lista_adiacenta[a][i]] = dist[a] + 1;  // incrementam cu 1 pentru a masura distanta de la nodul la care ne aflam la nodul adiacent curent
        }
    }

    // la final, aflam care este cel mai indepartat nod de nodul de la care am pornit parcurgerea
    for(int i = 1; i <= numar_noduri; i++)
        if(d < dist[i])
        {
            u = i;
            d = dist[i];
        }
}

int main()
{
 /*   Graf x;
    fout << "Output pentru problema \"BFS\": ";
    x.bfs_initializare();

    fout << "\nOutput-ul pentru problema \"DFS\": ";
    x.dfs_initializare();

    fout << "\nOutputul pentru problema \"Componente tare conexe\" este:\n";
    x.ctc_initializare();

    fout << "Output-ul pentru \"Havel-Hakimi\": ";
    x.havel_hakimi();

    fout << "\nOutput-ul pentru problema \"Sortare topologica\" este: ";
    x.sortare_topologica_initializare();*/

    Graf g;
    //fout << "\nOutput-ul pentru problema \"Componente biconexe\" este:\n";
    g.biconex_initializare();
/*
    Graf l;
    fout << "Output-ul pentru problema \"Critical Connections in a Network\": ";
    l.citire_leetcode();

    Graf p;
    fout << "Output-ul pentru problema \"Arbore partial de cost minim\" este:\n";
    p.citire();
    p.apm();

    Graf q;
    fout << "Output-ul pentru problema \"Paduri de multimi disjuncte\" este:\n";
    q.disjoint();

    Graf j;
    fout << "Output-ul pentru problema \"Algoritmul lui Dijkstra\" este: ";
    j.dijkstra1();

    Graf h;
    fout << "\nOutput-ul pentru problema \"Algoritmul Bellman-Ford\" este: ";
    h.bellman_ford1();

    Graf k;
    fout << "\nOutput-ul pentru problema \"Diametrul unui arbore\" este: ";
    k.citire_darb();
    k.bfs_darb_init();

    Graf a;
    fout << "\nOutput-ul pentru problema \"Floyd-Washall/Roy-Floyd\" este:\n";
    a.citire_roy_floyd();

    Graf b;
    fout << "Output-ul pentru problema \"Flux maxim\" este: ";
    b.citire_flux();
*/
    fin.close();
    fout.close();

    return 0;
}