Cod sursa(job #2822163)

Utilizator amalia.gemanGeman Aamalia amalia.geman Data 23 decembrie 2021 17:34:43
Problema Parcurgere DFS - componente conexe Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 25.78 kb
#include <bits/stdc++.h>
#define NMax 100001
#define inf 100000
#define NMax5 50005
#define NMax25 2500
#define NMax1 101
#define inf2 1005

using namespace std;

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


class graf
{
    int nrNoduri, nrMuchii;
    bool orientat;
    vector <int> gr[NMax]; // liste de adiacenta ale grafului


public:
    graf(int n, int m, bool o); // constructor
    void Citire_Orientat();
    void Citire_Neorientat();
    void Citire_Arbore();
    void Afisare_Graf();

    // BFS
    vector<int> BFS(int s, bool viz[NMax]); // s - nodul de start

    // DFS
    void DFS(int s, bool viz[NMax]); // s - nodul de start

    // Determinare nr de componente conexe cu DFS
    int Comp_Conexe(bool viz[NMax]); // returneaza cate componente conexe are graful

    // Kosaraju
    void Graf_Transpus(); // creeaza graful transpus in grT
    void ParcurgereGrafInitial(bool viz[NMax]); // parcurgerea in adancime a grafului initial
    void DFST(int s, bool vizDFST[NMax], vector<int> &aux); // s - nodul de start, dfs pe graful transpus
    vector<vector<int>> CTC(); // apeleasa DFST si afiseaza comp tare conexe

    // Algoritm Havel-Hakimi
    bool Havel_Hakimi(vector<int>grade, int nrNoduri);

    // Sortare topologica
    stack<int> Sortare_Topologica();

    // Afisare Muchii Critice
    void Afisare_Muchii_Critice();
    void DFS_Muchii_Critice(int s, int tata, bool viz[], int nivel[], int niv_min_acc[], int &ct_muchii_critice);

    // Afisare Componente Biconexe
    void Afisare_Componente_Biconexe();
    void DFS_Componente_Biconexe(int s, int tata, bool viz[], int nivel[], int niv_min_acc[], int &ct_biconexe, stack <int> &S2);

    // APM
    vector<pair<int,int>> Kruskal(int &cost, int &ct_muchii_adaugate);

    // Dijkstra
    vector<int> Dijkstra(int s);

    // Bellman-Ford
    bool Bellman_Ford(int s, vector<int>&sol);

    // Paduri de multimi disjuncte
    vector<bool> Disjoint(int parinte[NMax]);
    int Find(int x, int parinte[NMax]);
    void Union(int x, int y, int parinte[NMax]);

    //Roy-Floyd
    void Roy_Floyd(int matrice[NMax1][NMax1]);

    // Diametrul unui arbore
    int Diametru_Arbore();
    void BFS_Diametru_Arbore(int s, int &ultim, int &distanta_maxima);

    // Ciclu Eulerian
    void Ciclu_Eulerian();
    void Euler(int s, bool viz[NMax], int &ct, vector<int>&Sol);
};


int N, M;

//-----------Tema1---------------------------------------------------
// DFS

// BFS

// Muchii critice

// Componente biconexe

// Kosaraju
stack <int> S; // stiva pentru memorarea nodurilor in ordinea timpilor de final
vector <int> grT[NMax]; // liste de adiacenta ale grafului transpus

// Havel-Hakimi

// Muchii Critice + Componente Biconexe
vector <vector<int>> muchii_critice;

// Componente Biconexe
vector <int> componente_biconexe[NMax];


//-----------Tema2---------------------------------------------------
// Kruskal

// Dijkstra
vector<pair<int,int>> muchii[NMax25];

// Bellman-Ford

// Disjoint


//-----------Tema3---------------------------------------------------
// Roy-Floyd

// Diametru arbore


//-----------Tema4---------------------------------------------------
// Ciclu eulerian


graf :: graf(int n, int m, bool o)
{
    nrNoduri = n;
    nrMuchii = m;
    orientat = o;
}

void graf :: Citire_Orientat()
{
    for(int i = 1; i <= nrMuchii; ++i)
    {
        int x, y;
        fin >> x >> y;
        gr[x].push_back(y);
    }
}

void graf :: Citire_Neorientat()
{
    for(int i = 1; i <= nrMuchii; ++i)
    {
        int x, y;
        fin >> x >> y;
        gr[x].push_back(y);
        gr[y].push_back(x);
    }
}

void graf :: Citire_Arbore()
{
    for(int i = 1; i <= nrMuchii; ++i)
    {
        int x, y;
        fin >> x >> y;
        gr[x].push_back(y);
        gr[y].push_back(x);
    }
}

void graf :: Graf_Transpus()
{
    for(int i = 1; i <= nrNoduri; ++i)
        for(auto j : gr[i])
            grT[j].push_back(i);
}

void graf :: Afisare_Graf()
{
    for(int i=1; i <= nrNoduri; ++i)
    {
        fout << i << " : " ;
        for(auto j: gr[i])
            fout << j << " ";
        fout << "\n";
    }
}

vector <int> graf :: BFS(int s, bool viz[NMax])
{
    int distanta[NMax];
    vector <int> sol;
    queue <int> q;

    viz[s] = 1;
    q.push(s);

    while(!q.empty())
    {
        int k = q.front();  // elem din varful cozii
        q.pop();            // sterg elem din varful cozii
        for(auto i : gr[k])
            if(!viz[i])
            {
                viz[i] = 1;
                q.push(i);
                distanta[i] = distanta[k] + 1;
            }
    }

    for(int i = 1; i<= nrNoduri; ++i)
        sol.push_back(distanta[i]);

    return sol;
}

void graf :: DFS(int s, bool viz[NMax])
{
    viz[s] = 1;
    for(auto i : gr[s])
        if(!viz[i])
            DFS(i, viz);

    // pentru CTC(Kosaraju) - stabilim timpul de finalizare pentru nodul s
    S.push(s);
}

int graf :: Comp_Conexe(bool viz[NMax])
{
    int ct = 0;
    for(int i = 1; i <= nrNoduri; ++i)
        if(!viz[i])
        {
            ct++;
            DFS(i, viz);
        }
    return ct;
}

void graf :: ParcurgereGrafInitial(bool viz[NMax])
{
    for(int i = 1; i <= nrNoduri; ++i)
        if(!viz[i])
            DFS(i, viz);
}

void graf :: DFST(int s, bool vizDFST[NMax], vector<int> &aux)
{
    vizDFST[s] = 1;
    aux.push_back(s);

    for(auto i : grT[s])
        if(!vizDFST[i])
            DFST(i, vizDFST, aux);
}

vector<vector<int>> graf :: CTC()
{
    int ct_ctc = 0;
    bool vizDFST[NMax] = {0};

    vector<vector<int>> sol;

    while(!S.empty()) // luam nodurile din stiva si facem un dfs din nodul respectiv daca nodul nu a fost deja vizitat
    {
        // in acest caz se va forma o noua comp tare conexa
        int varf = S.top();
        S.pop();
        if(!vizDFST[varf])
        {
            ct_ctc ++;
            vector<int> aux;

            DFST(varf, vizDFST, aux);
            sol.push_back(aux);
        }
    }

    fout << ct_ctc << "\n";

    return sol;
}

bool myfunction(int i, int j)
{
    return (i>j);
}

bool graf :: Havel_Hakimi(vector <int> grade, int nrNoduri)
{
    bool ok =1;

    while(ok)
    {
        sort(grade.begin(), grade.end(), myfunction); // sortam descrescator

        if(grade[0] == 0) // daca cel mai mare element dupa sortare este 0 => toate elementele sunt 0 => se poate forma
            return true;

        if(grade[0] > grade.size() - 1) // daca gradul este mai mare decat (nr de noduri curente - 1 => NU se poate forma
            return false;

        int grad_curent = grade[0];
        grade.erase(grade.begin() + 0); // elimin primul element

        for(int i = 0; i < grad_curent; ++i) // scad 1 doar din primele grad_curent elemente
        {
            grade[i] --;
            if(grade[i] < 0)
                return false;
        }
    }
}

stack<int> graf :: Sortare_Topologica() // o parcurgere in adancime a grafului unde se determina timpii de finalizare pt fiecare nod
{
    // nodurile vor fi afisate in ordinea descrescatoare a timpilor de finalizare
    bool viz[NMax];
    ParcurgereGrafInitial(viz);

    return S;
}

void graf :: DFS_Muchii_Critice(int s, int tata, bool viz[], int nivel[], int niv_min_acc[], int &ct_muchii_critice)
{
    viz[s] = 1;
    nivel[s] = nivel[tata] + 1; // calculam nivelul pe care se afla nodul curent s
    niv_min_acc[s] = nivel[s]; // nivelul minim accesibil este acelasi cu nivelul pe care se afla nodul curent s pentru moment

    for(auto i : gr[s])
        if(i != tata)
        {
            if(viz[i] == 1)   // (s,i) (deci nu poate fi muchie critica) deoarece i a fost vizitat deja
            {
                if(niv_min_acc[s] > nivel[i])
                    niv_min_acc[s] = nivel[i];
            }
            else
            {
                DFS_Muchii_Critice(i, s, viz, nivel, niv_min_acc, ct_muchii_critice);

                if(niv_min_acc[s] > niv_min_acc[i])
                    niv_min_acc[s] = niv_min_acc[i];
            }

            // determinare muchii critice
            if(nivel[s] < niv_min_acc[i]) // conditie necesara ca (s,i) sa fie muchie critica
            {
                // (s, i)
                ct_muchii_critice++;
                vector <int> v1;
                v1.push_back(s);
                v1.push_back(i);
                muchii_critice.push_back(v1);
            }
        }
}

void graf :: Afisare_Muchii_Critice()
{
    bool viz[NMax] = {0};
    int nivel[NMax] = {0};
    int niv_min_acc[NMax] = {0};
    int ct_muchii_critice = 0;

    DFS_Muchii_Critice(1, 0, viz, nivel, niv_min_acc, ct_muchii_critice);

    fout << ct_muchii_critice << "\n";

    for(vector<vector<int>>::iterator i = muchii_critice.begin() ; i != muchii_critice.end(); i++)
    {
        fout << "[" ;
        vector<int>::iterator j = i->begin();
        fout<<*j;
        fout << ",";
        j++;
        fout <<*j;
        fout << "] ";
    }
}

void graf :: DFS_Componente_Biconexe(int s, int tata, bool viz[], int nivel[], int niv_min_acc[], int &ct_biconexe, stack <int> &S2)
{
    viz[s] = 1;
    S2.push(s); // adaugam nodurile in stiva S2 in oridinea in care le vizitam

    nivel[s] = nivel[tata] + 1; // calculam nivelul pe care se afla nodul curent s
    niv_min_acc[s] = nivel[s];  // nivelul minim accesibil este acelasi cu nivelul pe care se afla nodul curent s pentru moment

    for (auto i : gr[s])
    {
        if (viz[i] == 1)       // (s,i) (deci nu poate fi muchie critica) deoarece i a fost vizitat deja
        {
            if(niv_min_acc[s] > nivel[i])
                niv_min_acc[s] = nivel[i];
        }
        else
        {
            DFS_Componente_Biconexe(i, s, viz, nivel, niv_min_acc, ct_biconexe, S2);

            if(niv_min_acc[s] > niv_min_acc[i])
                niv_min_acc[s] = niv_min_acc[i];


            // determinare componente biconexe
            if(niv_min_acc[i] >= nivel[s]) // conditie necesara ca (s,i) sa fie muchie critica
            {
                ct_biconexe ++;

                while(!S2.empty() && S2.top() != i) // eliminam nodurile pana la nodul i
                {
                    componente_biconexe[ct_biconexe].push_back(S2.top());
                    S2.pop();
                }
                componente_biconexe[ct_biconexe].push_back(S2.top()); // adaugam si nodul i
                S2.pop();
                componente_biconexe[ct_biconexe].push_back(s);        // adaugam si nodul s
            }
        }
    }
}

void graf :: Afisare_Componente_Biconexe()
{
    bool viz[NMax] = {0};

    int nivel[NMax] = {0};
    int niv_min_acc[NMax] = {0};
    int ct_biconexe = 0;
    stack <int> S;

    DFS_Componente_Biconexe(1, 0, viz, nivel, niv_min_acc, ct_biconexe, S);

    fout << ct_biconexe << "\n";

    for(int i = 1; i <= ct_biconexe; ++i)
    {
        for(auto j : componente_biconexe[i])
            fout << j << " ";
        fout << "\n";
    }
}

vector<pair<int,int>> graf :: Kruskal(int &cost, int &ct_muchii_adaugate)
{
    pair< int, pair<int, int> > v[NMax]; // structura de date pt graful neorientat ponderat -> (c, {x,y})
    int C[NMax];                         // C[x] = numarul componentei din care face parte nodul x
    vector<pair<int,int>> Sol;           // structura de date pt arcele din APM

    // citire graf ponderat
    for(int i = 1; i <= nrMuchii; ++i)
    {
        fin >> v[i].second.first >> v[i].second.second; // muchia (x,y)
        fin >> v[i].first;                              // cu costul c
    }

    sort(v + 1, v + nrMuchii + 1); // sortam crescator muchiile in functie de cost

    for(int i = 1; i <= nrNoduri; ++i) C[i] = i; // initial se pleaca cu n arbori formati dintr-un singur nod

    for(int i = 1; i <= nrMuchii && ct_muchii_adaugate < nrNoduri - 1; ++i)
    {
        // verificam daca muchia i poate fi adaugata
        // cele doua extremitati trebuie sa faca parte din compomnente conexe diferite
        if(C[v[i].second.first] != C[v[i].second.second])
        {
            cost += v[i].first;      // marim costul arborelui

            ct_muchii_adaugate++;    // adaugam muchia de ordin i la arbore
            Sol.push_back({v[i].second.first,v[i].second.second});

            // unificare
            int cx = C[v[i].second.first];  // componenta din care face parte o extremitate -> x
            int cy = C[v[i].second.second]; // componente din care face parte cealalta extremitate -> y
            for(int j = 1; j <= nrNoduri; ++j)
                if(C[j] == cx) // pt toate nodurile care fac parte din componenta lui x le vom trece in componenta lui y
                    C[j] = cy;
        }
    }

    return Sol;
}

vector<int> graf :: Dijkstra(int s)
{
    priority_queue <pair<int,int>> q;   // coada cu prioritati {cost,nod}
    bool vizDij[NMax5] = {0};     // viz[x] = 1 daca nodul a fost vizitat
    int dist[NMax5];        // dist[x] = distanta de la nodul de start la nodul x
    vector <int> Sol;

    // initial presupunem fiecare distanta ca fiind infinit
    for(int i = 1; i <= N; ++i)
        dist[i] = inf;

    q.push({0,s});  // adaugam in coada nodul de inceput cu costul 0 (de la s la s avem distanta 0)
    vizDij[s] = 1;  // marcam nodul ca fiind vizitat
    dist[s] = 0;    // distanta de la s la s va fi 0

    while(!q.empty())
    {
        int nod_curent = q.top().second; // nodul curent
        q.pop();

        vizDij[nod_curent] = 0; // presupunem ca nodul curent nu a fost vizitat inca

        for(auto i : muchii[nod_curent])  // parcurgem nodurile adiacente cu nodul curent
        {
            int y = i.second;      // nodul adiacent cu nodul curent
            int cost = i.first;    // costul de la nodul curent  pana la y

            if(dist[nod_curent] + cost < dist[y])
            {
                dist[y] = dist[nod_curent] + cost;
                if(!vizDij[y]) // marcam nodul ca fiind vizitat daca nu era
                {
                    vizDij[y] = 1;
                    q.push({dist[y],y}); // adaugam din nou in coada pt ce ne ar putea imbunatati costul ulterior
                }
            }
        }
    }

    for(int i = 2; i <= nrNoduri; ++i)
    {
        if(dist[i] != inf)
            Sol.push_back(dist[i]);
        else
            Sol.push_back(0);
    }

    return Sol;
}

bool graf :: Bellman_Ford(int s, vector<int>&Sol)
{
    queue <int> C;
    vector <bool> adaugat(nrNoduri + 1, 0);
    vector <int> dist(nrNoduri + 1, inf);
    vector <int> viz(nrNoduri + 1, 0);
    bool ciclu_negativ = 0;

    // declarare si citire graf ponderat
    vector <vector <pair<int, int> >> muchii(nrNoduri + 1);
    for(int i = 1; i <= nrMuchii; ++i)
    {
        int x, y, cost;
        fin >> x >> y >> cost;
        muchii[x].push_back({cost,y});
    }

    C.push(s);          // adaugam nodul s in coada
    dist[s] = 0;        // distanta de la s la s va fi 0
    adaugat[s] = 1;     // marcam cu 1 nodul ca fiind adaugat in coada

    while(!C.empty() && !ciclu_negativ)
    {
        int nod_curent = C.front();
        C.pop();
        adaugat[nod_curent] = 0; // punem 0 pt ca nodul a fost eliminat din coada


        for(int i = 0; i < muchii[nod_curent].size(); ++i) // parcurgem nodurile adiacente cu nodul curent
        {
            int y = muchii[nod_curent][i].second;   // nodul destinatie
            int cost = muchii[nod_curent][i].first; // costul de la nodul curent la nodul destinatie

            if(dist[nod_curent] + cost < dist[y])   // recalculam distanta minima daca este necesar
            {
                dist[y] = dist[nod_curent] + cost;

                if(!adaugat[y]) // adaugam nodul in coada daca nu exista deja
                {
                    C.push(y);
                    adaugat[y] = 1;
                }

                viz[y] ++;
                if(viz[y] >= nrNoduri) // cazul pt care se formeaza ciclu negativ
                {
                    ciclu_negativ = 1;
                    break;
                }
            }
        }
    }

    if(!ciclu_negativ)
    {
        for(int i = 2; i <= nrNoduri; ++i)
            Sol.push_back(dist[i]);
    }
    return ciclu_negativ;
}

int graf :: Find(int x, int parinte[NMax]) // determina radacina arborelui din care face parte nodul x
{
    if(parinte[x] == x) // inseamna ca x insusi este radacina
        return x;
    else
    {
        parinte[x] = Find(parinte[x], parinte); // apeleaza recursiv pana ajunge la radacina
        return parinte[x];
    }
}

void graf :: Union(int x, int y, int parinte[NMax]) // se reunesc multimile celor doua numere x si y
{
    // unim radacina lui x cu radacina lui y (adaugand rad lui y la rad lui x)
    int a, b;
    a = Find(x, parinte);
    b = Find(y, parinte);
    parinte[b] = a;
}

vector<bool> graf :: Disjoint(int parinte[NMax])
{
    vector<bool> Sol;
    for(int i = 1; i <= nrNoduri; ++i) // initial fiecare nod este propriul sau parinte
        parinte[i] = i;

    for(int i = 1; i <= M; ++i)
    {
        int op, x, y;
        fin >> op >> x >> y;

        if(op == 1) // se reunesc multimile nodurilor x si y, adica vor avea acelasi parinte
            Union(x, y, parinte);
        else
        {
            int a = Find(x, parinte);  // daca nodurile x si y au aceeasi radacina atunci sunt din aceeasi multime
            int b = Find(y, parinte);
            if(a == b)
                Sol.push_back(true);
            else
                Sol.push_back(false);
        }
    }
    return Sol;
}

void graf :: Roy_Floyd(int matrice[NMax1][NMax1])
{
    for(int k = 1; k <= nrNoduri; ++k)
        for(int i = 1; i <= nrNoduri; ++i)
            for(int j = 1; j <= nrNoduri; ++j)
                if(matrice[i][j] > matrice[i][k] + matrice[k][j])
                    matrice[i][j] = matrice[k][j] + matrice[i][k];

    // afisare
    for(int i = 1; i <= nrNoduri; ++i)
    {
        for(int j = 1; j <= nrNoduri; ++j)
            if(matrice[i][j] != inf2)
                fout << matrice[i][j] << " ";
            else fout << 0 << " ";
        fout << "\n";
    }
}

void graf :: BFS_Diametru_Arbore(int s, int &capat, int &distanta_maxima)
{
    int distanta[NMax];
    queue <int> q;
    bool viz[NMax] = {0};

    viz[s] = 1;
    distanta[s] = 1;
    q.push(s);

    while(!q.empty())
    {
        int k = q.front();  // elem din varful cozii
        q.pop();            // sterg elem din varful cozii
        for(auto i : gr[k])
            if(!viz[i])
            {
                viz[i] = 1;
                q.push(i);
                distanta[i] = distanta[k] + 1;
            }
    }

    distanta_maxima = 0;
    for(int i = 1; i<= nrNoduri; ++i)
        if(distanta[i] > distanta_maxima)
        {
            distanta_maxima = distanta[i];
            capat = i;
        }
}

int graf :: Diametru_Arbore()
{
    int capat1, capat2, distanta_maxima;
    BFS_Diametru_Arbore(1, capat1, distanta_maxima); // facem un BFS din primul nod si aflam capatul cel mai indepartat si distanta pana la capatul cel mai indepartat
    BFS_Diametru_Arbore(capat1, capat2, distanta_maxima); // facem un BFS din capatul rezultat in apelul anterior si aflam capatul cel mai indepartat de acesta si distanta dintre ele

    return distanta_maxima;
}

void graf :: Euler(int s, bool viz[NMax], int &ct, vector<int>&Sol)
{
    while (!muchii[s].empty()) // luam pe rand fiecare nod adiacent cu nodul s
    {
        int vecin = muchii[s].back().second; // nod vecin cu nodul s
        int muchie = muchii[s].back().first; // index muchie
        muchii[s].pop_back();

        if(viz[muchie] == 0)
        {
            viz[muchie] = 1;
            Euler(vecin, viz, ct, Sol);
        }
    }
    ct++;
    Sol.push_back(s);
}

void graf :: Ciclu_Eulerian()
{
    for (int i = 1; i <= nrMuchii; ++i) // citire muchii
    {
        int x, y;
        fin >> x >> y;
        muchii[x].push_back({i, y});
        muchii[y].push_back({i, x});
    }

    for (int i = 1; i <= nrNoduri; ++i) // verificam gradul fiecarui nod sa fie par
        if (muchii[i].size() % 2)
        {
            fout << "-1";
            return;
        }

    bool viz[NMax] = {0};
    int ct = 0;
    vector<int> Sol;

    Euler(1, viz, ct, Sol);

    for (auto i : Sol)
        fout << i << " ";
}



int main()
{
    cout << "Alegeti task-ul:\n Task 1: DFS - Afisare numar componente conexe\n Task 2: BFS\n Task 3: Determinare CTC\n Task 4: Havel-Hakimi\n";
    cout << " Task 5: Sortare topologica\n Task 6: Afisare muchii critice\n Task 7: Componente biconexe\n Task 8: APM\n Task 9: Dijkstra\n Task 10: Bellman-Ford\n Task 11: Disjoint\n";
    cout << " Task 12: Roy-Floyd\n Task 13: Diametru arbore\n";
    cout << " Task 14: Ciclu Eulerian\n\n";
    cout << "Scrie task-ul : ";

    int task;
    task = 1;

    if(task == 1) // DFS - nr comp conexe
    {
        bool viz[NMax] = {0};
        fin >> N >> M;
        graf G(N, M, 0);
        G.Citire_Neorientat();
        int nr_componente_conexe = G.Comp_Conexe(viz);
        fout << nr_componente_conexe << "\n";
    }
    else if(task == 2) // BFS
    {
        int s;
        bool viz[NMax];
        fin >> N >> M >> s;
        graf G(N, M, 1);
        G.Citire_Orientat();
        vector <int> distante = G.BFS(s, viz);

        int poz = 1;
        for(auto i : distante)
            if(viz[poz])
                fout << i << " ", poz++;
            else
                fout << -1 << " ", poz++;
    }

    else if(task == 3) // Componente tare conexe
    {
        bool viz[NMax] = {0};
        vector<vector<int>> sol;

        fin >> N >> M;
        graf G(N, M, 1);
        G.Citire_Orientat();
        G.Graf_Transpus();
        G.ParcurgereGrafInitial(viz);
        sol = G.CTC();

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

    else if(task == 4) // Havel - Hakimi
    {
        fin >> N;
        graf G(N, 0, 0);

        vector <int> grade;

        for(int i = 1; i <= N; ++i)
        {
            int x;
            fin >> x;
            grade.push_back(x);
        }

        bool ok = G.Havel_Hakimi(grade, N);
        if(ok == true)
            fout << "DA se poate construi un graf.";
        else fout << "NU se poate construi un graf. ";
    }

    else if(task == 5) // Sortare Topologica
    {
        fin >> N >> M;
        graf G(N, M, 1);

        stack <int> sol;

        G.Citire_Orientat();
        sol = G.Sortare_Topologica();

        while(!sol.empty())
        {
            fout << sol.top() << " ";
            sol.pop();
        }
    }
    else if(task == 6) // Muchii critice
    {
        fin >> N >> M;
        graf G(N, M, 0);
        G.Citire_Neorientat();
        G.Afisare_Muchii_Critice();
    }
    else if(task == 7) // Componente biconexe
    {
        fin >> N >> M;
        graf G(N, M, 0);
        G.Citire_Neorientat();
        G.Afisare_Componente_Biconexe();
    }
    else if(task == 8) // APM - Kruskal
    {
        fin >> N >> M;
        graf G(N, M, 0);

        int cost = 0, ct_muchii_adaugate = 0; // costul minim total, numarul de muchii adaugate in subgraful de cost minim determinat
        vector <pair<int,int>> Sol;
        Sol = G.Kruskal(cost, ct_muchii_adaugate);

        // afisare cost, nr muchii si muchii
        fout << cost << "\n" << ct_muchii_adaugate << "\n";
        for(auto i : Sol)
            fout << i.first << " " << i.second << "\n";
    }
    else if(task == 9) // Dijkstra
    {
        fin >> N >> M;
        graf G(N, M, 0);

        vector <int> Sol;

        //citire
        for(int i = 1; i <= M; ++i)
        {
            int x, y, cost;
            fin >> x >> y >> cost;
            muchii[x].push_back({cost,y});
        }

        // apel functie + afisare
        Sol = G.Dijkstra(1);
        for(auto i : Sol)
            fout << i << " ";
    }
    else if(task == 10) // Bellman-Ford
    {
        fin >> N >> M;
        graf G(N, M, 0);

        vector<int>Sol;

        bool ciclu_negativ = G.Bellman_Ford(1, Sol);

        if(ciclu_negativ == 0)
            for(auto i: Sol)
                fout << i << " ";
        else
            fout << "Ciclu negativ!";
    }
    else if(task == 11) // Disjoint
    {
        fin >> N >> M;
        graf G(N, 0, 0);

        int parinte[NMax];
        vector<bool>Sol;
        Sol = G.Disjoint(parinte);


        for(auto i : Sol)
            if(i == 1)
                fout << "DA\n";
            else fout << "NU\n";
    }
    else if(task == 12) // Roy-Floyd
    {
        fin >> N;
        graf G(N, 0, 0);

        int matrice[NMax1][NMax1];

        // citire
        for(int i = 1; i <= N; ++i)
            for(int j = 1; j <= N; ++j)
                fin >> matrice[i][j];

        // initializare
        for(int i = 1; i <= N; ++i)
            for(int j = 1; j <= N; ++j)
                if(i == j)
                    matrice[i][j] = 0;
                else if(i != j && matrice[i][j] == 0)
                    matrice[i][j] = inf2;

        G.Roy_Floyd(matrice);
    }
    else if(task == 13) // Diametru arbore
    {
        fin >> N;
        graf G(N, N-1, 0); // arbore
        G.Citire_Arbore();
        int d = G.Diametru_Arbore();
        fout << d;
    }
    else if(task == 14) // Ciclu Eulerian
    {
        fin >> N >> M;
        graf G(N, M, 0);
        G.Ciclu_Eulerian();
    }
    return 0;
}