Cod sursa(job #2814358)

Utilizator MadalinaKopaczMadalina Kopacz MadalinaKopacz Data 7 decembrie 2021 23:05:11
Problema Componente biconexe Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 26.33 kb
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

struct muchie
{
    int x, y; //noduri
    int cost;
    int capacitate;
};

///Clasa pentru reprezentare de grafuri
class Graf
{
private:
    int nrNoduri;
    int nrMuchii;
    bool orientat; //1 = orientat, 0 = neorientat
    bool cuCost; //1 = cu cost, 0 = fara cost
    bool cuCapacitate; //1 = cu capacitati pt fluxuri, 0 = fara capacitati pt fluxuri

    vector<vector<muchie>> lisAdiacenta;

    void DFS(const int nodStart, vector<bool> &vizitat, stack<int> &stiva);
    void ctcDFS(int x, vector<int> &disc, vector<int> &low, stack<int> &stiva, vector<bool> &gasitStiva, vector<vector<int>> &rez);
    void critConDFS(int x, vector<bool> &viz, vector<int> &disc, vector<int> &low, vector<int> &tata, vector<vector<int>> &rez);
    void biconexeDFS(int x, int tata, vector<int> &disc, vector<int> &low, stack<int> &comp, string &rez, int &nrCompBiconexe, vector<bool> &viz);
public:
    Graf(const int nrNoduriDat, const int nrMuchiiDat, const bool orientat, const bool cuCostDat, const bool cuCapacitate);
    Graf(const Graf &grafDat);
    ~Graf();

    int getNrNoduri();
    int getNrMuchii();

    Graf& operator= (const Graf &grafDat);
    friend std::ostream& operator<<(std::ostream &out, const Graf &grafDat);
    void citireGraf(istream &in);

    vector<int> BFS(const int nodStart);   //returneaza dist min de la un nod la celelalte
    int nrCompConexe();                    //returneaza nr de comp con
    pair<int, string> Biconex();
    /*static int findDis(int elem, vector<muchie> &multimi);
    static void unionDis(const int x, const int y, vector<muchie> &multimi);



    stack<int> sortareTopologica();        //ret stiva sortarii topologice
    static bool existaGraf(vector<int> &grade);
    vector<vector<int>> CTC();
    vector<vector<int>> CriticalConnections();


      GrafCuCost(const int nrNoduriDat, const int nrMuchiiDat);
    GrafCuCost(const int nrNoduriDat, const int nrMuchiiDat, const vector<vector<pair<int, int>>> &listaData);
    GrafCuCost(const GrafCuCost &grafDat);//constructor copiere
    ~GrafCuCost();

    vector<pair<int, pair<int, int>>> getLisMuchii();

    GrafCuCost& operator= (const GrafCuCost &grafDat);
    friend std::ostream& operator<<(std::ostream &out, const GrafCuCost &grafDat);

    void citireOrientatCC(istream &in);
    void citireNeorientatCC(istream &in);

    pair<int, vector<pair<int, int>>> Kruskal(vector<pair<int, pair<int, int>>> muchii);
    vector<int> BellmanFord(const int start);
    vector<int> Dijkstra(const int start);*/
};
Graf :: Graf(const int nrNoduriDat, const int nrMuchiiDat, const bool orientatDat, const bool cuCostDat, const bool cuCapacitateDat)  //constructor parametrizat - nu avem lista de adiacenta
{
    nrNoduri = nrNoduriDat;
    nrMuchii = nrMuchiiDat;
    orientat = orientatDat;
    cuCost = cuCostDat;
    cuCapacitate = cuCapacitateDat;
    vector<muchie> aux(1,{-1, -1, -1, -1});       //initializam prima pozitie cu -1 pentru ca indexarea e de la 1
    for(int i = 0; i <= nrNoduri+1; ++i) {
        lisAdiacenta.push_back(aux);
    }
}

Graf :: Graf(const Graf & grafDat)   //constructor copiere
{
    nrNoduri = grafDat.nrNoduri;
    nrMuchii = grafDat.nrMuchii;
    orientat = grafDat.orientat;
    cuCost = grafDat.cuCost;
    cuCapacitate = grafDat.cuCapacitate;
    lisAdiacenta = grafDat.lisAdiacenta;
}

Graf :: ~Graf()
{
    lisAdiacenta.clear();
}

int Graf :: getNrNoduri()    //getter numar noduri
{
    return nrNoduri;
}

int Graf :: getNrMuchii()    //getter numar muchii
{
    return nrMuchii;
}
/*
int Graf :: findDis(int elem, vector<muchie> &multimi)   //gasim reprezentantul multimii respectice
                                                                 //care e de fapt radacina arborelui
{
    int radacina = elem;
    while(multimi[radacina].y != radacina) {
        radacina= multimi[radacina].y;
    }
    return radacina;
}

void Graf :: unionDis(const int x, const int y, vector<muchie> &multimi)
{
    int radX = findDis(x, multimi), radY = findDis(y, multimi);     //vedem care sunt reprezentantii fiecarei multimi

    if(multimi[radX].cost > multimi[radY].cost) {     //legam arborede mic de cel mai mare
        multimi[radX].cost = multimi[radX].cost + multimi[radY].first;
        multimi[radY].first = radX;
        multimi[radY].second = multimi[radX].second;
    } else {
        multimi[radY].second = multimi[radY].second + multimi[radX].second;
        multimi[radX].first = radY;
        multimi[radX].second = multimi[radY].second;
    }
}
*/

///SUPRAINCARCARI OPERATORI
Graf& Graf :: operator= (const Graf &grafDat)//supraincarcare operator egal
{
    if(this != &grafDat) {
        this->nrNoduri = grafDat.nrNoduri;
        this->nrMuchii = grafDat.nrMuchii;

        this->orientat = grafDat.orientat;
        this->cuCost = grafDat.cuCost;
        this->cuCapacitate = grafDat.cuCapacitate;
        if(!this->lisAdiacenta.empty())
            lisAdiacenta.clear();

        this->lisAdiacenta = grafDat.lisAdiacenta;
    }
    return *this;
}

ostream& operator<< (std::ostream& out, const Graf& grafDat)  //supraincarcare operator afisare
{
    out << "Numarul de noduri ale grafului este: " << grafDat.nrNoduri << "\n";
    out << "Numarul de muchii ale grafului este: " << grafDat.nrMuchii << "\n";

    for (int i = 1; i <= grafDat.nrNoduri; ++i) {
        out << i << ": ";
        for (int j = 1; j < grafDat.lisAdiacenta[i].size(); ++j) {
            out <<"{" << grafDat.lisAdiacenta[i][j].x << " " << grafDat.lisAdiacenta[i][j].y<<" "<< grafDat.lisAdiacenta[i][j].cost<<" " << grafDat.lisAdiacenta[i][j].capacitate<<"} ";
        }
        out<<"\n";
    }

    return out;
}

///METODE CITIRI

void Graf :: citireGraf(istream &in)  //citim de la tastatura sau din fisier un graf neorientat
{
    int x, y, cost, capacitate;

    for(int i = 1; i <= nrMuchii; ++i) {
        in >> x >> y;
        muchie m;
        m.x = x; m.y = y;
        if (cuCost) {
            in >> cost;
            m.cost = cost;
        }
        else {
            m.cost = -1;
        }

        if (cuCapacitate) {
            in >> capacitate;
            m.capacitate = capacitate;
        }
        else {
            m.capacitate = -1;
        }

        lisAdiacenta[m.x].push_back(m);
        if (!orientat) {
                swap(m.x, m.y);
                lisAdiacenta[m.x].push_back(m);
        }
    }
}

///Afisare distante BFS
vector<int> Graf :: BFS(const int nodStart)     //parcurgere in latime
{
    queue<int> Q;
    int x, y;
    vector<int> distanta(nrNoduri+1, -1);

    distanta[nodStart] = 0;
    Q.push(nodStart);

    while(!Q.empty()) {
        x = Q.front();   //eliminam nodul curent din coada
        Q.pop();

        for (int i = 1; i < lisAdiacenta[x].size(); ++i)
            if (distanta[lisAdiacenta[x][i].y] == -1) {              //daca nu a fost vizitat vecinul
                distanta[lisAdiacenta[x][i].y] = distanta[x] + 1;
                Q.push(lisAdiacenta[x][i].y);
            }
    }

    return distanta;
}

///Aflare nr componente conexe
void Graf:: DFS(const int x, vector<bool> &vizitat, stack<int> &stiva)   //parcurgere in adancime
{
    vizitat[x] = 1;

    for(int i = 1; i < lisAdiacenta[x].size(); i++)
        if(vizitat[lisAdiacenta[x][i].y] == 0)    //daca vecinul sau nu a fost vizitat, mergem la el
            DFS(lisAdiacenta[x][i].y, vizitat, stiva);
    stiva.push(x);
}

int Graf :: nrCompConexe()   //numar de componente conexe facut cu DFS
{
    stack<int> stiva;       //implementata formal, pentru a putea utiliza DFS ul si la sortareTopologica()
    int nrCompCon = 0;
    vector<bool> vizitat(nrNoduri+1, 0);

    for(int i = 1; i <= nrNoduri; ++i )
        if(!vizitat[i]) {
            nrCompCon++;
            DFS(i, vizitat, stiva);
        }

    return nrCompCon;
}

///Biconex
//https://www.geeksforgeeks.org/biconnected-components/
void Graf :: biconexeDFS(int x, int tata, vector<int> &disc, vector<int> &low, stack<int> &comp, string &rez, int &nrCompBiconexe, vector<bool> &viz)
{
    viz[x] = 1;
    disc[x] = disc[tata] + 1;
    low[x] = disc[x];

    for (int i = 1; i < lisAdiacenta[x].size(); ++i)
        if (lisAdiacenta[x][i].y != tata) {
            if (!viz[lisAdiacenta[x][i].y]) {
                comp.push(lisAdiacenta[x][i].y);
                biconexeDFS(lisAdiacenta[x][i].y, x, disc, low, comp, rez, nrCompBiconexe, viz);

                low[x] = min(low[x], low[lisAdiacenta[x][i].y]);   //conexiune a fiului cu un stramos al lui x
                if (disc[x] <= low[lisAdiacenta[x][i].y]) {   //am gasit o muchie critica
                    nrCompBiconexe++;
                    comp.push(x);
                    while (!comp.empty() && comp.top() != lisAdiacenta[x][i].y) {
                        rez += (to_string(comp.top()) + " ");
                        comp.pop();
                    }
                    if (!comp.empty()) {
                        rez += (to_string(comp.top()) + " ");
                        comp.pop();
                    }
                    rez += "\n";
                }
            } else if (disc[lisAdiacenta[x][i].y] < low[x]) //daca e vizitat si are timpul de disc mai mic, atunci modificam timp min x
                low[x] = disc[lisAdiacenta[x][i].y];
        }
}

pair<int, string> Graf :: Biconex()
{
    vector<int> disc(nrNoduri+1, -1);
    vector<int> low(nrNoduri+1, -1);
    vector<bool> viz(nrNoduri + 1, 0);
    stack<int> comp;
    string rez = "";

    int nrCompBiconexe = 0;

    disc[1] = 1;
    biconexeDFS(2, 1, disc, low, comp, rez, nrCompBiconexe, viz);

    return make_pair(nrCompBiconexe, rez);
}


/*



///Afisare sortare topologica
stack<int> GrafFaraCost :: sortareTopologica()  //afisam o sortare topologica posibila
{
    stack<int> stiva;
    vector<bool> vizitat(nrNoduri+1, 0);

    for(int i = 1; i <= nrNoduri; ++i )
        if(!vizitat[i])
            DFS(i, vizitat, stiva);

    return stiva;

}


///algoritmul Havel-Hakimi pt a afla daca o secventa de numere poate forma graf simplu
//https://www.geeksforgeeks.org/find-if-a-degree-sequence-can-form-a-simple-graph-havel-hakimi-algorithm/
//O(n^2)
bool GrafFaraCost :: existaGraf(vector<int> &grade)
{
    vector<int> copieGrade;
    copieGrade = grade;

    int S = 0;      //suma grade
    for (int i = 0; i < copieGrade.size(); ++i)
        S += copieGrade[i];
    if (S % 2 == 1) {    //suma gradelor intr-un graf neorientat e mereu para
        return 0;
    }

    sort(grade.begin(), grade.end(), greater<int>());  //luam gradele in ordine descrescatoare
    while (true) {

        if (grade[0] == 0) {
            return 1;
        }

        int fst = grade[0];
        grade.erase(grade.begin() + 0);
        if (fst > grade.size()) {  //daca avem un grad mai mare decat numarul de noduri ramase
            return 0;
        }

        for (int i = 0; i < fst; ++i) {
            grade[i]--;
            if (grade[i] < 0) {
                return 0;
            }
        }

        int j = fst;                            //reordonam descrescator
        for (int i = 0; i < fst; ++i)   {       //fiind deja ordonate descrescator
            if(j >= grade.size())               //atunci cand scadem 1 valorile nu vor mai fi ordonate decat daca sunt egale initial
                break;                           //si fst e mai mic decat numarul de valori egale intial
            if (grade[i] < grade[j]) {
                swap(grade[i], grade[j]);
                j++;
            }
        }
    }

}

///Componente Tare Conexe - Algoritmul lui Tarjan
//https://www.geeksforgeeks.org/tarjan-algorithm-find-strongly-connected-components/
void GrafFaraCost::ctcDFS(int x, vector<int> &disc, vector<int> &low, stack<int> &stiva, vector<bool> &gasitStiva, vector<vector<int>> &rez)
{
    static int timp = 0;

    disc[x] = low[x] = ++timp;
    stiva.push(x);
    gasitStiva[x] = 1;


    for (int i = 1; i < lisAdiacenta[x].size(); i++) {
        if (disc[lisAdiacenta[x][i]] == -1)    {
            ctcDFS(lisAdiacenta[x][i], disc, low, stiva, gasitStiva, rez);
            low[x] = min(low[x], low[lisAdiacenta[x][i]]);   //conexiune a fiului cu un stramos al lui x
        } else if (gasitStiva[lisAdiacenta[x][i]] == 1)
            low[x] = min(low[x], disc[lisAdiacenta[x][i]]);  //muchie de intoarcere
    }

    vector<int> compNoua;
    if (low[x] == disc[x]) {       //daca am gasit nod de start pt componenta tare conexa scoatem nodurile din stiva
        while (stiva.top() != x) {
            compNoua.push_back(stiva.top());
            gasitStiva[stiva.top()] = 0;
            stiva.pop();
        }

        compNoua.push_back(stiva.top());
        gasitStiva[stiva.top()] = 0;
        rez.push_back(compNoua);
        stiva.pop();
    }
}

vector<vector<int>> GrafFaraCost :: CTC()
{
    vector<int> disc(nrNoduri+1, -1);
    vector<int> low(nrNoduri+1, -1);
    stack<int> stiva;
    vector<bool> gasitStiva(nrNoduri+1, 0);
    vector<vector<int>> rez;

    for (int i = 1; i <= nrNoduri; ++i) {
        if (disc[i] == -1)
            ctcDFS(i, disc, low, stiva, gasitStiva, rez);
    }

    return rez;
}

///Muchii Critice
//https://www.geeksforgeeks.org/bridge-in-a-graph/
void GrafFaraCost::critConDFS(int x, vector<bool> &viz, vector<int> &disc, vector<int> &low, vector<int> &tata, vector<vector<int>> &rez)
{
    static int timp = 0;

    viz[x] = 1;
    disc[x] = low[x] = ++timp;

    for (int i = 1; i < lisAdiacenta[x].size(); ++i) {
        if (!viz[lisAdiacenta[x][i]])    {
            tata[lisAdiacenta[x][i]] = x;
            critConDFS(lisAdiacenta[x][i], viz, disc, low, tata, rez);
            low[x] = min(low[x], low[lisAdiacenta[x][i]]);    //conexiune a fiului cu un stramos al lui x

            if (low[lisAdiacenta[x][i]] > disc[x]) {           //daca nu putem ajunge in fiu altfel (are timpul minim mai mare decat timpul de desc)
                vector<int> aux;
                aux.push_back(x);
                aux.push_back(lisAdiacenta[x][i]);
                rez.push_back(aux);
            }
        } else if (lisAdiacenta[x][i] != tata[x])  //daca e vizitat deja si nu e parinte, modificam valoarea lui low[x]
            low[x] = min(low[x], disc[lisAdiacenta[x][i]]);
    }
}

vector<vector<int>> GrafFaraCost :: CriticalConnections()
{
    vector<bool> viz(nrNoduri+1, 0);
    vector<int> disc(nrNoduri+1);
    vector<int> low(nrNoduri+1);
    vector<int> tata(nrNoduri+1, -1);
    vector<vector<int>> rez;

    for (int i = 1; i <= nrNoduri; ++i) {
        if (!viz[i])
            critConDFS(i, viz, disc, low, tata, rez);
    }
    return rez;
}


///CONSTRUCTORI
GrafCuCost :: GrafCuCost(const int nrNoduriDat, const int nrMuchiiDat)  : Graf(nrNoduriDat, nrMuchiiDat)
{
    vector<pair<int, pair<int, int>>> auxM(1);
    lisMuchii = auxM;
    vector<pair<int, int>> aux(1, {-1, -1});      //initializam prima pozitie cu -1 pentru ca indexarea e de la 1
    for(int i = 0; i <= nrNoduri+1; ++i) {
        lisAdiacenta.push_back(aux);
    }
}
GrafCuCost :: GrafCuCost(const int nrNoduriDat, const int nrMuchiiDat, const vector<vector<pair<int,int>>> &listaData)  : Graf(nrNoduriDat, nrMuchiiDat)
{
    lisAdiacenta = listaData;
}

GrafCuCost :: GrafCuCost(const GrafCuCost & grafDat) : Graf(grafDat) //constructor copiere
{
    lisAdiacenta = grafDat.lisAdiacenta;
}

///DESTRUCTOR
GrafCuCost :: ~GrafCuCost()
{
    lisAdiacenta.clear();
    lisMuchii.clear();
}

vector<pair<int, pair<int, int>>> GrafCuCost :: getLisMuchii()
{
    return lisMuchii;
}

///SUPRAINCARCARI OPERATORI
GrafCuCost& GrafCuCost :: operator= (const GrafCuCost &grafDat)//supraincarcare operator egal
{
    if(this != &grafDat) {
        this->nrNoduri = grafDat.nrNoduri;
        this->nrMuchii = grafDat.nrMuchii;

        if(!this->lisAdiacenta.empty())
            lisAdiacenta.clear();

        this->lisAdiacenta = grafDat.lisAdiacenta;
    }

    return *this;
}

ostream& operator<< (std::ostream& out, const GrafCuCost& grafDat)  //supraincarcare operator afisare
{
    out << "Numarul de noduri ale grafului este: " << grafDat.nrNoduri << "\n";
    out << "Numarul de muchii ale grafului este: " << grafDat.nrMuchii << "\n";

    for (int i = 1; i <= grafDat.nrNoduri; ++i) {
        out << i << ": ";
        for (int j = 1; j < grafDat.lisAdiacenta[i].size(); ++j) {
            out << " (" << grafDat.lisAdiacenta[i][j].first << ", " << grafDat.lisAdiacenta[i][j].second << ") ";
        }
        out<<"\n";
    }

    return out;
}

///METODE CITIRI

void GrafCuCost :: citireNeorientatCC(istream &in)  //citim de la tastatura sau din fisier un graf neorientat
{
    int x, y, c;

    for(int i = 1; i <= nrMuchii; ++i) {
        in >> x >> y >> c;
        lisAdiacenta[x].push_back({y, c}), lisAdiacenta[y].push_back({x, c});
        lisMuchii.push_back({c, {x, y}});
    }
}



void GrafCuCost :: citireOrientatCC(istream &in)   //citim de la tastatura sau din fisier un graf orientat
{
    int x, y, c;

    for(int i = 1; i <= nrMuchii; ++i) {
        in >> x >> y >> c;
        lisAdiacenta[x].push_back({y, c});
        lisMuchii.push_back({c, {x, y}});
    }
}

pair<int, vector<pair<int, int>>> GrafCuCost :: Kruskal(vector<pair<int, pair<int, int>>> muchii)
{
    int N = nrNoduri, M = nrMuchii;
    vector<pair<int, int>> multimi(N + 1, {0, 0});
    vector<pair<int,int>> rezMuchii(1, {-1, -1});
    int costTotal = 0;

    for(int i = 1; i <= N; ++i) {
        multimi[i] = {i, 1};
    }

    sort(muchii.begin() + 1, muchii.end());    //sortam muchiile

    for(int i = 1; i <= M && rezMuchii.size() < N; ++i) {
        if( findDis(muchii[i].second.first, multimi) != findDis(muchii[i].second.second, multimi) ) {
            unionDis(muchii[i].second.first, muchii[i].second.second, multimi);
            costTotal = costTotal + muchii[i].first;
            rezMuchii.push_back({muchii[i].second.first, muchii[i].second.second});
        }
    }

    return {costTotal, rezMuchii};

}

vector<int> GrafCuCost :: BellmanFord(const int start)
{
    vector<int> distBMF(nrNoduri + 1, INF);
    vector<int> viz(nrNoduri + 1, 0);
    vector<bool> apartCoada(nrNoduri + 1, false);
    queue<int> coada;
    int faraCiclNeg = 1;

    coada.push(start), apartCoada[start] = 1;
    distBMF[start] = 0;

    while (!coada.empty() && faraCiclNeg) {
        int x = coada.front();
        coada.pop();
        apartCoada[x] = 0;

        for (int i = 1; i < lisAdiacenta[x].size(); i++)
            if (distBMF[x] + lisAdiacenta[x][i].second < distBMF[lisAdiacenta[x][i].first]) {
                distBMF[lisAdiacenta[x][i].first] = distBMF[x] + lisAdiacenta[x][i].second;      //relaxam
                viz[lisAdiacenta[x][i].first]++;

                if(!apartCoada[lisAdiacenta[x][i].first]) {   //daca nu am mai parcurs nodul resp
                    coada.push(lisAdiacenta[x][i].first);
                    apartCoada[lisAdiacenta[x][i].first] = 1;
                }
                if(viz[lisAdiacenta[x][i].first] >= nrNoduri) {   //verificam daca avem ciclu
                    faraCiclNeg = 0;
                }
            }
    }
    if(!faraCiclNeg)
        distBMF.clear();
    return distBMF;
}

vector<int> GrafCuCost :: Dijkstra(const int start)
{
    //minHeap ordonat dupa cost modelat cu priority queue
    priority_queue< pair<int, int>, vector <pair<int, int> >, greater<pair<int, int>>> pQueue;
    vector<int> distDij(nrNoduri + 1, INF);
    vector<bool> apartPQ(nrNoduri+ 1, 0);
    pQueue.push(make_pair(0, start)), distDij[start] = 0;

    while (!pQueue.empty()) {
        int x = pQueue.top().second;
        pQueue.pop();
        if (!apartPQ[x]) {     //sa nu vizitam acelasi nod de mai multe ori
            apartPQ[x] = 1;
            for (int i = 1; i < lisAdiacenta[x].size(); i++)
                if (distDij[lisAdiacenta[x][i].first] > distDij[x] + lisAdiacenta[x][i].second) {     //verificam daca am gasit cost mai mic
                    distDij[lisAdiacenta[x][i].first] = distDij[x] + lisAdiacenta[x][i].second;
                    pQueue.push(make_pair(distDij[lisAdiacenta[x][i].first], lisAdiacenta[x][i].first));
                }
        }
    }
    return distDij;
}


///functii sablon utilizata cand sunt citite de la tastatura nr de noduri, nr de muchii si muchiile


GrafCuCost CitireGrafCC(string numeFisier, string tipGraf) //https://infoarena.ro/problema/dfs - 100pc
{
    ifstream fin(numeFisier);

    int N, M;
    fin >> N >> M;

    GrafCuCost graf(N, M);
    if(tipGraf == "neorientat") graf.citireNeorientatCC(fin);
    else graf.citireOrientatCC(fin);
    fin.close();
    return graf;
}

///Corpurile functiilor

void Rezolva_SortareTopologica(string fisierIntrare, string fisierIesire)   //https://infoarena.ro/problema/sortaret - 100pc
{
    ofstream fout(fisierIesire);
    stack<int> rez = CitireGrafFC(fisierIntrare, "orientat").sortareTopologica();
    while (!rez.empty()) {
        fout << rez.top() << " ";
        rez.pop();
    }
    fout.close();
}

//exemple: (DA:(5 5 5 5 5 5),(5 5 5 5 4 4),(3 3 3 3),(4 4 4 4 4),(2 2 1 1),(2 2 1 1 0),(2 0 1 2 1),(2 1 2 1 2 1 2 3)
//(NU:(5 5 5 5 5 4),(3 2 1 0), (3 3 3 3 3)
void Rezolva_Havel_Hakimi(string fisierIntrare, string fisierIesire)
{
    ifstream fin(fisierIntrare);
    ofstream fout(fisierIesire);
    vector<int> grade;
    int N, x;
    fin >> N;

    for (int i = 0; i < N; i++) {
        fin >> x;
        grade.push_back(x);
    }
    fin.close();
    if(GrafFaraCost :: existaGraf(grade)) fout << "Da";
    else fout << "Nu";
    fout.close();
}

void Rezolva_CTC(string fisierIntrare, string fisierIesire)      //https://infoarena.ro/problema/ctc - 100 pc
{
    vector<vector<int>> rez = CitireGrafFC(fisierIntrare, "orientat").CTC();
    ofstream fout(fisierIesire);
    fout << rez.size() << "\n";
    for (int i = 0; i < rez.size(); ++i) {
        for (int j = 0; j < rez[i].size(); ++j) {
            fout << rez[i][j] << " ";
        }
        fout << "\n";
    }
    fout.close();
}


void Rezolva_Critical_Connection()    //https://leetcode.com/problems/critical-connections-in-a-network/  - Accepted
{
    int N, M;
    cin >> N >> M;

    GrafFaraCost graf(N, M);
    graf.citireNeorientatFC(cin);
    vector<vector<int>> rez =  graf.CriticalConnections();

    cout << "[";
    for(int i = 0; i < rez.size() ; ++i) {
        cout << "[" << rez[i][0] << "," << rez[i][1] << "]";
    }
    cout << "]";
}



void Rezolva_APM(string fisierIntrare, string fisierIesire)    //https://www.infoarena.ro/problema/apm  - 100 pc
{
    ofstream fout(fisierIesire);
    GrafCuCost graf = CitireGrafCC(fisierIntrare, "neorientat");
    vector<pair<int, pair<int, int>>> muchii =  graf.getLisMuchii();
    pair<int, vector<pair<int, int>>> rez = graf.Kruskal(muchii);

    fout << rez.first << "\n";
    fout << rez.second.size() - 1 << "\n";
    for(int i = 1; i < rez.second.size(); ++i)
        fout << rez.second[i].first << " " << rez.second[i].second << "\n";
}


void Rezolva_Dijkstra(string fisierIntrare, string fisierIesire)    // https://www.infoarena.ro/problema/dijkstra  - 100 pc
{
    ofstream fout(fisierIesire);
    vector<int> distDij = CitireGrafCC(fisierIntrare, "orientat").Dijkstra(1);

    for(int i = 2; i < distDij.size(); i++)
        if(distDij[i] == INF) fout << 0 << " ";
        else fout << distDij[i] << " ";
}

void Rezolva_BMF(string fisierIntrare, string fisierIesire)    // https://www.infoarena.ro/problema/bellmanford  - 100 pc
{
    ofstream fout(fisierIesire);
    vector<int> distBMF = CitireGrafCC(fisierIntrare, "orientat").BellmanFord(1);

    if(distBMF.size()) {
        for(int i = 2; i < distBMF.size(); i++) {
            fout << distBMF[i] << " ";
        }
    } else
        fout << "Ciclu negativ!";
}

void Rezolva_Disjoint(string fisierIntrare, string fisierIesire)  //https://www.infoarena.ro/problema/disjoint - 100 pc
{
    ifstream fin(fisierIntrare);
    ofstream fout(fisierIesire);

    int N, M;
    fin >> N >> M;
    vector<bool> rez;
    vector<pair<int, int>> multimi(N + 1, {0,0}); //indexare de la 1

    for(int i = 1; i <= N; ++i)
        multimi[i].first = i, multimi[i].second = 1;

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

        if (task == 1) {
            if(Graf::findDis(x, multimi) != Graf::findDis(y, multimi)) {
                Graf::unionDis(x, y, multimi);
            }
        } else {
            if(Graf::findDis(x, multimi) == Graf::findDis(y, multimi))
                fout << "DA\n";
            else
                fout << "NU\n";
        }
    }
    fin.close();
    fout.close();
}
*/

Graf CitireGraf(string numeFisier, bool orientat, bool cuCost, bool cuCapacitate) //https://infoarena.ro/problema/dfs - 100pc
{
    ifstream fin(numeFisier);

    int N, M;
    fin >> N >> M;

    Graf graf(N, M, orientat, cuCost, cuCapacitate);
    graf.citireGraf(fin);
    fin.close();
    return graf;
}

void Rezolva_BFS(string fisierIntrare, string fisierIesire)  //https://www.infoarena.ro/problema/bfs - 100pc
{
    ifstream fin(fisierIntrare);
    ofstream fout(fisierIesire);

    int N, M, S;
    fin >> N >> M >> S;

    Graf graf(N, M, 1, 0, 0);
    graf.citireGraf(fin);

    vector<int> rez = graf.BFS(S);
    for(int i = 1; i < rez.size() ; ++i) {
        fout << rez[i] << " ";
    }
    fout.close();
}

void Rezolva_DFSComponenteConexe(string fisierIntrare, string fisierIesire) //https://infoarena.ro/problema/dfs - 100pc
{
    ofstream fout(fisierIesire);
    fout << CitireGraf(fisierIntrare, 0, 0, 0).nrCompConexe();
    fout.close();
}

void Rezolva_Biconex(string fisierIntrare, string fisierIesire)    // https://infoarena.ro/problema/biconex  - 100 pc
{
    pair<int, string> rez = CitireGraf(fisierIntrare, 0, 0, 0).Biconex();
    ofstream fout(fisierIesire);
    fout << rez.first << "\n";
    fout << rez.second;
    //Graf graf = CitireGraf(fisierIntrare, 0, 0, 0);
    //fout << graf;
    fout.close();
}

///Driver code
int main()
{
    Rezolva_Biconex("biconex.in", "biconex.out");
    return 0;
}