Cod sursa(job #2797635)

Utilizator redikusTiganus Alexandru redikus Data 10 noiembrie 2021 12:43:09
Problema Componente tare conexe Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 17.27 kb
#include <iostream>
#include <fstream>
#include <bits/stdc++.h>

using namespace std;

class graf {

    int noduri, muchii;
    vector < vector < int >> adiacenta;

public:
    // Constructor cu matrice de adiacenta data
    graf(vector < vector < int >> listaAdiacenta, int noduri, int muchii): adiacenta(listaAdiacenta), noduri(noduri), muchii(muchii) {};

    // Constructor fara matrice de adiacenta, se cu initalizeaza una goala
    graf(int noduri, int muchii): adiacenta(noduri + 1), noduri(noduri), muchii(muchii) {};

    void citireNeorientat(istream & in ) {
        for (int i = 0; i < muchii; i++) {
            int aux1, aux2; in >> aux1 >> aux2;
            adiacenta[aux1].push_back(aux2);
            adiacenta[aux2].push_back(aux1);
        }
    }

    void citireOrientat(istream & in ) {
        for (int i = 0; i < muchii; i++) {
            int aux1, aux2; in >> aux1 >> aux2;
            adiacenta[aux1].push_back(aux2);
        }
    }

    vector < int > bfs(int start) {
        vector < int > rasp(noduri + 1, -1);
        vector < int > vis(noduri + 1);
        queue < int > q;
        vis[start] = 1;
        q.push(start);
        rasp[start] = 0;

        // Se baga pe rand in queue toate nodurile nevizitate ale nodului din capul queue-ului, si se noteaza timpul la care se ajunge la ele
        while (!q.empty()) {
            int curr = q.front();
            q.pop();
            for (int i: adiacenta[curr])
                if (vis[i] == 0) {
                    vis[i] = 1;
                    q.push(i);
                    rasp[i] = rasp[curr] + 1;
                };
        }
        return rasp;
    }

    int dfs() {
        vector < int > vis(noduri + 1);
        int res = 0;
        stack < int > s;


        // Se baga pe rand in stack toate nodurile nevizitate ale nodului din capul queue-ului, si se noteaza timpul la care se ajunge la ele. Se simuleaza call stack-ul de la recursivitate de dfs
        for (int i = 1; i <= noduri; i++) {
            if (vis[i] == 0) {
                res++;
                vis[i] = 1;
                s.push(i);
                while (!s.empty()) {
                    int curr = s.top();
                    s.pop();
                    for (int i = adiacenta[curr].size() - 1; i >= 0; i--) {
                        int nod = adiacenta[curr][i];
                        if (vis[nod] == 0) {
                            vis[nod] = 1;
                            s.push(nod);
                        }
                    }
                }
            }
        }
        return res;
    }

    void dfsbiconex(int tata, vector < int > & timp, vector < int > & rev, stack < pair < int, int >> & s, int & timpcurent, vector < string > & rasp) {
        // Tarjan modificat
        timpcurent++;
        timp[tata] = timpcurent;
        rev[tata] = timpcurent;
        int fiuRadacina = 0;

        // Se tin minte timpii de intoarcere respectiv timpul de intalnire al fiecarui nod in arborele de dfs call
        for (int fiu: adiacenta[tata]) {
            if (timp[fiu] == -1) {
                // Se retine cati fii are fiecare nod in arborele de call dfs
                fiuRadacina++;
                // Se parcurge prin dfs fiecare nod, si cand se gaseste un nod nevizitat se adauga muchia intr-un stack
                s.push(make_pair(tata, fiu));
                dfsbiconex(fiu, timp, rev, s, timpcurent, rasp);
                // Timpul de revenire al unei muchii este minimul dintre timpul de revenire curent si timpul de revenire al fiului,
                // pt ca daca fiul are timp de revenire mai mic si tata il va putea accesa
                rev[tata] = min(rev[tata], rev[fiu]);

                if ((timp[tata] != 1 && rev[fiu] >= timp[tata]) || (timp[tata] == 1 && fiuRadacina > 1)) {
                    // Se verifica daca un nod e critic, si sunt 2 cazuri. Primul e cand nodul nu e chiar nodul de incepere al dfs, iar timpul de revenire al fiului este mai mare ca tatal.
                    // Practic cand nu suntem intr-un ciclu. Al doilea caz este cel discutat la laborator, cand nodul este chiar radacina arborelui, daca are 2 fii atunci e clar nod critic
                    pair < int, int > stop = make_pair(tata, fiu);
                    unordered_set < int > aux;
                    string str = "";

                    // Se face afisare cu string
                    aux.insert(stop.first);
                    aux.insert(stop.second);
                    str += to_string(stop.first);
                    str += " ";
                    str += to_string(stop.second);
                    str += " ";
                    // Se parcurge stackul de muchii pana cand dam de muchia care leaga fiul de tata.
                    // Se face asta pt ca mai intai se va adauga o muchie intre fiu si tata in stack, apoi se va face dfs
                    // si se vor detecta componentele biconexe ale descendentilor si se vor adauga in stack muchiile descendentilor, iar cad se va iesi din dfs-ul descendentilor unui nod tata
                    // ce vor ramane in stack vor fi chiar muchiile corespunzatoare componentei biconexe, pana la muchia care leaga tatal de fiu
                    while (s.top() != stop) {
                        int nodcur = s.top().first;
                        if (aux.find(nodcur) == aux.end()) {
                            str += to_string(nodcur);
                            str += " ";
                            aux.insert(s.top().first);
                        }
                        s.pop();
                    }
                    str += '\n';
                    rasp.push_back(str);
                    s.pop();
                }
            }
            else {
                // Daca nodul e deja vizitat, atunci am detectat o muchie de intoarcere,
                // si timpul de revenire al tatalui devine minimul dintre timpul actual de revenire (ca poate fi mai mic, cu o alta muchie gasita deja)
                // si timpul de gasire al fiului
                rev[tata] = min(rev[tata], timp[fiu]);
                // Verificam sa nu fie o muchie care sa duca in fata (in arbore) de fapt, pt ca deja a fost analizata mai devreme in dfs
                if (timp[fiu] < timp[tata]) {
                    s.push(make_pair(tata, fiu));
                }
            }
        }
    }

    vector < string > biconex() {
        vector < int > timp(noduri + 1, -1), rev(noduri + 1);
        stack < pair < int, int >> s;
        vector < string > rasp;
        int timpo = 0;

        // Se porneste dfs-ul din toate componentele conexe, si apoi se adauga la raspuns si ce a mai ramas in stack, pt ca si ce ramane va forma o componenta biconexa
        for (int i = 0; i < noduri; i++) {
            if (timp[i] == -1) {
                dfsbiconex(i, timp, rev, s, timpo, rasp);
            }
            if (!s.empty()) {
                unordered_set < int > aux;
                string str;

                while (!s.empty()) {
                    if (aux.find(s.top().first) == aux.end()) {
                        aux.insert(s.top().first);
                        str += to_string(s.top().first);
                        str += " ";
                    }
                    if (aux.find(s.top().second) == aux.end()) {
                        aux.insert(s.top().second);
                        str += to_string(s.top().second);
                        str += " ";
                    }
                    s.pop();
                }
                str += '\n';
                rasp.push_back(str);
            }
        }
        return rasp;
    }

    void dfsCtc(int tata, vector < int > & timp, vector < int > & rev, stack < int > & s, int & timpcurent, vector < string > & rasp, unordered_set < int > & check) {
        // Tarjan
        timpcurent++;
        timp[tata] = timpcurent;
        rev[tata] = timpcurent;
        s.push(tata);
        check.insert(tata);

        // Se tin minte timpii de intoarcere respectiv timpul de intalnire al fiecarui nod in arborele de dfs call
        for (int fiu: adiacenta[tata]) {
            if (timp[fiu] == -1) {
                // Cand descoperim un fiu nevizitat facem dfs
                dfsCtc(fiu, timp, rev, s, timpcurent, rasp, check);
                // Timpul de revenire al unei muchii este minimul dintre timpul de revenire curent si timpul de revenire al fiului,
                // pt ca daca fiul are timp de revenire mai mic si tata il va putea accesa
                rev[tata] = min(rev[tata], rev[fiu]);
            }
            // Daca nodul e deja vizitat, atunci am detectat o muchie de intoarcere,
            // si timpul de revenire al tatalui devine minimul dintre timpul actual de revenire (ca poate fi mai mic, cu o alta muchie gasita deja)
            // si timpul de gasire al fiului
            else if (check.find(fiu) != check.end()) {
                rev[tata] = min(rev[tata], timp[fiu]);
            }
        }
        // Dupa ce se parcurg toti descendentii tatalui, verificam daca timpul de revenire al tatalui este egal cu timpul de intalnire al sau.
        // Daca sunt egale, exista doua cazuri, ori tatal face parte dintr-o componenta tare conexa cu descendenti de-ai sai bagati deja in stack,
        // ori el este o componenta tare conexa singur.
        // Se vor salva nodurile rezultate in stringuri. Check tine minte daca un element e in stack sau nu, caci daca nu e poate forma o componenta tare conexa cu altcineva
        if (timp[tata] == rev[tata]) {
            string aux = "";
            while (s.top() != tata) {
                aux += to_string(s.top());
                aux += " ";
                check.erase(s.top());
                s.pop();
            }
            aux += to_string(s.top());
            aux += " ";
            check.erase(s.top());
            s.pop();
            aux += '\n';
            rasp.push_back(aux);
        }
    }

    vector < string > ctc() {
        vector < int > timp(noduri + 1, -1), rev(noduri + 1);
        unordered_set < int > check;
        stack < int > s;
        vector < string > rasp;
        int timpo = 0;

        // Se porneste dfs-ul din toate componentele conexe, si apoi se adauga la raspuns si ce a mai ramas in stack, pt ca si ce ramane va forma o componenta biconexa
        for (int i = 1; i <= noduri; i++) {
            if (timp[i] == -1) {
                dfsCtc(i, timp, rev, s, timpo, rasp, check);
            }
        }
        return rasp;
    }

    void dfsSortaret(int tata, vector < int > & vis, vector < int > & res) {
        // Se face dfs cu recursivitate
        for (int fiu: adiacenta[tata]) {
            if (!vis[fiu]) {
                vis[fiu] = 1;
                dfsSortaret(fiu, vis, res);
            }
        }
        // Cand se termina de cautat in fii unui nod, se adauga la raspuns, pt ca inseamna ca nu mai depinde nimeni de el
        res.push_back(tata);
    }

    vector < int > sortaret() {
        vector < int > vis(this -> noduri + 1);
        vector < int > res;

        // Se face dfs pt fiecare componenta conexa
        for (int i = 1; i <= this -> noduri; i++) {
            if (vis[i] == 0) {
                vis[i] = 1;
                dfsSortaret(i, vis, res);
            }
        }
        return res;
    }

    void dfsCriticalConnections(int tata, vector < int > & timp, vector < int > & rev, int & timpcurent, vector < vector < int >> & rasp, vector < int > & parinte) {
        // Tarjan modificat
        timpcurent++;
        timp[tata] = timpcurent;
        rev[tata] = timpcurent;

        // Se tin minte timpii de intoarcere respectiv timpul de intalnire al fiecarui nod in arborele de dfs call
        for (int fiu: adiacenta[tata]) {
            // Tinem minte parintele direct din arborele dfs
            parinte[fiu] = tata;
            if (timp[fiu] == -1) {
                // Cand descoperim un fiu nevizitat facem dfs
                dfsCriticalConnections(fiu, timp, rev, timpcurent, rasp, parinte);
                // Timpul de revenire al unei muchii este minimul dintre timpul de revenire curent si timpul de revenire al fiului,
                // pt ca daca fiul are timp de revenire mai mic si tata il va putea accesa
                rev[tata] = min(rev[tata], rev[fiu]);
                if (rev[fiu] > timp[tata]) {
                    vector < int > aux;
                    aux.push_back(tata);
                    aux.push_back(fiu);
                    rasp.push_back(aux);
                }
            }
            // Daca nodul e deja vizitat, atunci am detectat o muchie de intoarcere,
            // si timpul de revenire al tatalui devine minimul dintre timpul actual de revenire (ca poate fi mai mic, cu o alta muchie gasita deja)
            // si timpul de gasire al fiului
            // Se verifica ca muchia sa nu fie una prezenta deja, sa nu fie una care sa mearga in fata in arborele dfs
            else if (parinte[tata] != fiu) {
                rev[tata] = min(rev[tata], timp[fiu]);
            }
        }
    }

    vector < vector < int >> criticalConnections() {
        vector < int > timp(noduri + 1, -1), rev(noduri + 1), parinte(noduri + 1);
        vector < vector < int >> rasp;
        int timpo = 0;

        // Se face dfs pt fiecare componenta conexa
        for (int i = 1; i <= noduri; i++) {
            if (timp[i] == -1) {
                dfsCriticalConnections(i, timp, rev, timpo, rasp, parinte);
            }
        }
        return rasp;
    }
};

bool havelHakimi(vector < int > & a) {
    // Mai intai se sorteaza vectorul ca sa avem mereu nodul cu cel mai mare grad primul
    sort(a.begin(), a.end(), [](int m, int n) {
        return m > n;
    });
    int x = a[0];

    // Daca mai e un singur nod in vector cu grad nenul, evident, nu se va putea face graf cu el
    if (a.size() == 1 && x >= 1) {
        return false;
    }
    // Daca primul nod e 0, tinand cont ca vectorul e sortat crescator, atunci toate sunt 0 si e bine, se poate forma graf
    if (x == 0) {
        return true;
    }

    // Se sterge primul element din vector, ii facem legaturile
    a.erase(a.begin() + 0);

    // Daca nodul are grad mai mare decat sunt legaturi posibile atunci e clar ca nu e posibil sa facem graf
    if (x > a.size()) {
        return false;
    }
    // 'Conectam' primul nod la urmatoarele noduri din vector, cate ne spune gradul lui
    for (int i = 0; i < x; i++) {
        a[i]--;
        // Daca un nod ajunge cu gradul pe minus, e clar ca nu se paote face graf
        if (a[i] < 0) {
            return false;
        }
    }
    // Apelam recursiv cu noul vector format
    return havelHakimi(a);
}

void bfsDriver() {
    // Citire
    ifstream in ("bfs.in");
    ofstream out("bfs.out");

    int n, m, s; in >> n >> m >> s;
    graf x(n, m);

    x.citireOrientat( in );

    // Afisare
    vector < int > rasp = x.bfs(s);
    for (int i = 1; i <= n; i++) {
        out << rasp[i] << " ";
    }

    in.close();
    out.close();
}

void dfsDriver() {
    // Citire
    ifstream in ("dfs.in");
    ofstream out("dfs.out");

    int n, m; in >> n >> m;
    graf x(n, m);

    x.citireNeorientat( in );

    // Afisare
    out << x.dfs();

    in.close();
    out.close();
}

void biconexDriver() {
    // Citire
    ifstream in ("biconex.in");
    ofstream out("biconex.out");

    int n, m; in >> n >> m;
    graf x(n, m);

    x.citireNeorientat( in );

    // Afisare
    vector < string > fin = x.biconex();
    out << fin.size() << '\n';
    for (string i: fin) {
        out << i;
    }

    in.close();
    out.close();
}

void ctcDriver() {
    // Citire
    ifstream in ("ctc.in");
    ofstream out("ctc.out");

    int n, m; in >> n >> m;
    graf x(n, m);

    x.citireOrientat( in );

    // Afisare
    vector < string > fin = x.ctc();
    out << fin.size() << '\n';
    for (string i: fin) {
        out << i;
    }

    in.close();
    out.close();
}

void sortaretDriver() {
    // Citire
    ifstream in ("sortaret.in");
    ofstream out("sortaret.out");

    int n, m; in >> n >> m;
    graf x(n, m);

    x.citireOrientat( in );

    // Afisare
    // Citim vectorul invers fata de cum l-am creat, pt ca ultimul nod care e accesat e nodul cu cele mai multe dependente
    vector < int > res = x.sortaret();
    for (int i = res.size() - 1; i >= 0; i--) {
        out << res[i] << " ";
    }

    in.close();
    out.close();
}

void havelHakimiDriver() {
    // Citire
    ifstream in ("havelhakimi.in");
    ofstream out("havelhakimi.out");

    int n; in >> n;
    vector < int > a;

    // Afisare
    for (int i = 0; i < n; i++) {
        int aux; in >> aux;
        a.push_back(aux);
    }
    out << havelHakimi(a);

    in.close();
    out.close();
}

void criticalConnectionsDriver() {
    // Citire
    int n, m;
    cin >> n >> m;
    graf x(n, m);

    x.citireNeorientat(cin);

    // Afisare
    vector < vector < int >> fin = x.criticalConnections();
    for (auto i: fin) {
        for (auto j: i) {
            cout << j << " ";
        }
    }
}

int main() {
    // Se apeleaza functia corespunzatoare problemei
    ctcDriver();
    return 0;
}