Cod sursa(job #2797501)

Utilizator ioana2008vIoana Velniceru ioana2008v Data 10 noiembrie 2021 00:20:49
Problema BFS - Parcurgere in latime Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 14.54 kb
#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <queue>
#include <utility>
#include <algorithm>

using namespace std;

ifstream fi("bfs.in");
ofstream fo("bfs.out");

vector<bool> vizitat; //vector pentru marcarea nodurilor vizitate in
                             //functiile componente_conexe si DFS; declarat
                             //global pentru a fi accesibil oricand din
                             //ambele functii

vector<int> nivel; //stocheaza adancimea la care a fost gasit un nod
                   //daca un element are valoarea -1, atunci nodul asociat
                   //este nevizitat
vector<int> low; //stocheaza nivelul minim la care poate ajunge un anumit
                 //nod mergand in fii pe drumuri de intoarcere
                 //(folosit si la componente tare conexe, prin analogie)
stack<pair<int, int>> muchii_comp_biconexe; //stocheaza muchii care vor intra
                                            //intr-o viitoare componenta
                                            //biconexa
//sunt declarati global pentru a fi accesibili oricand din functia
//componente_biconexe
vector<vector<int>> comp_biconexe; //declarat global pentru a fi accesibil
                                   //si din functia gasire_componenta_biconexa,
                                   //si din main
int index_ctc = 0;
vector<int> index;
vector<bool> inComponenta;
stack<int> noduri_comp_tare_conexa;
vector<vector<int>> comp_tare_conexe; //declarate global pentru a fi accesibile
                                      //oricand din functiile
                                      //componente_tare_conexe si
                                      //DFS_tare_conexe


class Graf
{
    int nr_noduri;
    int nr_muchii;
    int nod_start;
    vector<vector<int>> lista_adiacenta;
    void DFS(int nod);
    void gasire_componenta_biconexa(int nod1, int nod2);
    void DFS_tare_conexe(int nod);
public:
    Graf();
    void citire_neorientat();
    void citire_orientat();
    void citire_orientat_bfs();
    void BFS();
    void componente_conexe();
    void componente_biconexe(int nod_curent, int nod_initial, int nv);
    void componente_tare_conexe();
    void havel_hakimi();
};

Graf::Graf(){
    nr_noduri = 0;
    nr_muchii = 0;
    nod_start = 0;
}

void Graf::citire_neorientat(){
    int nod_1, nod_2;
    fi >> nr_noduri >> nr_muchii;
    for (int i = 1; i <= nr_noduri + 1; i++){
        lista_adiacenta.push_back(vector<int>());
    }
    for (int i = 0; i < nr_muchii; i++){
        fi >> nod_1 >> nod_2;
        lista_adiacenta[nod_1].push_back(nod_2);
        lista_adiacenta[nod_2].push_back(nod_1);
    }
}

void Graf::citire_orientat(){
    int nod_1, nod_2;
    fi >> nr_noduri >> nr_muchii;
    for (int i = 1; i <= nr_noduri + 1; i++){
        lista_adiacenta.push_back(vector<int>());
    }
    for (int i = 0; i < nr_muchii; i++){
        fi >> nod_1 >> nod_2;
        lista_adiacenta[nod_1].push_back(nod_2);
    }
}

void Graf::citire_orientat_bfs(){ //functie de citire specifica problemei BFS
                                  //pentru a permite citirea nodului de start
    int nod_1, nod_2;
    fi >> nr_noduri >> nr_muchii >> nod_start;
    for (int i = 1; i <= nr_noduri + 1; i++){
        lista_adiacenta.push_back(vector<int>());
    }
    for (int i = 0; i < nr_muchii; i++){
        fi >> nod_1 >> nod_2;
        lista_adiacenta[nod_1].push_back(nod_2);
    }
}

void Graf::BFS(){
    //declaram un vector "vizitat" pentru marcarea nodurilor vizitate, o
    //coada "coada_varfuri" pentru prelucrarea in ordine a nodurilor
    //parcurse, un vector "nivel" care stocheaza nivelurile pe care se afla
    //fiecare nod si o variabila "nod" pentru nodul care trebuie prelucrat
    bool vizitat[nr_noduri + 1];
    queue<int> coada_varfuri;
    int nod;
    int nivel[nr_noduri + 1];
    //initializam vectorul vizitat si vectorul nivel
    for (int i = 1; i <= nr_noduri; i++){
        vizitat[i] = false;
        nivel[i] = -1;
    }
    //incepem prin a pune nodul de start in coada, marcandu-l drept vizitat
    //si setandu-i nivelul ca fiind 0
    coada_varfuri.push(nod_start);
    vizitat[nod_start] = true;
    nivel[nod_start] = 0;
    //facem BFS pana cand nu mai sunt noduri de prelucrat
    while (!coada_varfuri.empty()){
        //luam un nod din coada
        nod = coada_varfuri.front();
        //pentru fiecare vecin al sau
        for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
            //daca vecinul respectiv nu a mai fost vizitat, il punem in
            //coada, il marcam drept vizitat si ii setam nivelul ca fiind
            //cel al nodului initial + 1
            if (!vizitat[*i]){
                coada_varfuri.push(*i);
                vizitat[*i] = true;
                nivel[*i] = nivel[nod] + 1;
            }
        }
        //eliminam nodul din coada
        coada_varfuri.pop();
    }
    //afisam nivelul tuturor nodurilor dupa BFS
    for (int i = 1; i <= nr_noduri; i++){
        fo << nivel[i] << " ";
    }
}

void Graf::DFS(int nod){
    //marcam nodul curent ca fiind vizitat
    vizitat[nod] = true;
    //pentru fiecare vecin al sau
    for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
        //daca vecinul respectiv nu a mai fost vizitat, apelam DFS pe acesta
        if (!vizitat[*i]){
            DFS(*i);
        }
    }
}

void Graf::componente_conexe(){
    //declaram un vector "vizitat" pentru marcarea nodurilor vizitate si o
    //variabila "nr_comp_conexe" in care stocam numarul de componente conexe
    //gasite (egal cu numarul de DFS-uri apelate din aceasta functie)
    int nr_comp_conexe = 0;
    //initializam vectorul vizitat
    for (int i = 1; i <= nr_noduri; i++){
        vizitat.push_back(false);
    }
    //luam fiecare nod, si daca nu a mai fost vizitat, apelam DFS pornind
    //de la el si incrementam numarul de componente conexe gasite
    for (int i = 1; i <= nr_noduri; i++){
        if (!vizitat[i]){
            nr_comp_conexe++;
            DFS(i);
        }
    }
    //la sfarsit, afisam numarul de componente conexe cerut
    fo << nr_comp_conexe;
}

void Graf::gasire_componenta_biconexa(int nod1, int nod2){
    //eliminam muchii din stiva pana ajungem la muchia (nod1, nod2)
    //cream un vector in care retinem componenta biconexa (muchiile sale,
    //mai tarziu vom pastra doar nodurile)
    vector<int> comp;
    int n1, n2;
    do {
        n1 = muchii_comp_biconexe.top().first;
        n2 = muchii_comp_biconexe.top().second;
        comp.push_back(n1);
        comp.push_back(n2);
        //stergem muchia din stiva
        muchii_comp_biconexe.pop();
    } while (n1 != nod1 && n2 != nod2);
    //punem componenta in lista de componente
    comp_biconexe.push_back(comp);
}

void Graf::componente_biconexe(int nod_curent, int nod_initial, int nv){
    //setam adancimea si low[nod_curent]; initial, nodul curent are low
    //egal cu adancimea
    if (nivel.empty() && low.empty()){
        for (int i = 1; i <= nr_noduri; i++){
            nivel.push_back(-1);
            low.push_back(-1);
        }
    }
    nivel[nod_curent] = nv;
    low[nod_curent] = nv;
    //pentru fiecare vecin al nodului
    for (auto i = lista_adiacenta[nod_curent].begin(); i != lista_adiacenta[nod_curent].end(); i++){
        //daca intalnim nodul initial printre vecini, il ignoram
        if (*i == nod_initial){
            continue;
        }
        //daca intalnim un nod pe care nu l-am vizitat
        if (nivel[*i] == -1){
            //punem muchia dintre cele doua noduri in stiva
            muchii_comp_biconexe.push(make_pair(nod_curent, *i));
            //apelam DFS (reapelam aceasta functie) pe nodul gasit
            //nv va avea valoarea nv + 1
            componente_biconexe(*i, nod_curent, nv + 1);
            //dupa ce iesim din recursie, va trebui sa actualizam
            //low[nod_curent] cu low-ul nodului gasit
            low[nod_curent] = min(low[nod_curent], low[*i]);
            //daca nodul gasit nu poate ajunge la un stramos al nodului curent
            if (low[*i] >= nivel[nod_curent]){
                //am gasit o componenta biconexa, deci incepem sa o retinem
                gasire_componenta_biconexa(nod_curent, *i);
            }
        } else {
            //altfel, actualizam low[nod_curent] cu adancimea nodului gasit
            low[nod_curent] = min(low[nod_curent], nivel[*i]);
        }
    }
}

void Graf::DFS_tare_conexe(int nod){
    //initializam nodul si low[nod] cu index-ul curent, punem nodul in stiva,
    //ii marcam prezenta in stiva si setam indexul pentru urmatorul nod
    index[nod] = index_ctc;
    low[nod] = index_ctc;
    index_ctc++;
    noduri_comp_tare_conexa.push(nod);
    inComponenta[nod] = true;
    //pentru fiecare vecin al nodului
    for (auto i = lista_adiacenta[nod].begin(); i != lista_adiacenta[nod].end(); i++){
        //daca nodul nu a fost vizitat, apelam DFS pe acest nod si actualizam
        //low-ul nodului curent cu low-ul nodului gasit
        if (index[*i] == -1){
            DFS_tare_conexe(*i);
            low[nod] = min(low[nod], low[*i]);
        } else if (inComponenta[*i]){
            //altfel, daca nodul este in stiva (si deci in componenta curenta),
            //actualizam low-ul nodului curent cu indexul nodului gasit
            low[nod] = min(low[nod], index[*i]);
        }
    }
    //daca low-ul unui nod este egal cu indexul sau, atunci inseamna ca el e
    //nodul "radacina" si ca trebuie sa formam componenta tare conexa din
    //acest nod
    if (low[nod] == index[nod]){
        //cream vectorul cu nodurile din componenta tare conexa extragandu-le
        //din stiva si stergandu-le prezenta in stiva; adaugam apoi acest
        //vector printre componente
        vector<int> comp;
        int n;
        do {
            n = noduri_comp_tare_conexa.top();
            comp.push_back(n);
            inComponenta[n] = false;
            noduri_comp_tare_conexa.pop();
        } while (n != nod);
        comp_tare_conexe.push_back(comp);
    }
}

void Graf::componente_tare_conexe(){
    if (index.empty() && low.empty() && inComponenta.empty()){
        for (int i = 1; i <= nr_noduri; i++){
            index.push_back(-1);
            low.push_back(-1);
            inComponenta.push_back(false);
        }
    }
    for (int i = 1; i <= nr_noduri; i++){
        if (index[i] == -1){
            DFS_tare_conexe(i);
        }
    }
}

void Graf::havel_hakimi(){
    //declaram un vector secv_grade, care va stoca gradele pe care trebuie
    //sa le aiba fiecare nod
    vector<int> secv_grade;
    //declaram o variabila suma_grade pentru a verifica daca suma gradelor
    //este para sau nu
    int n, suma_grade = 0;
    fi >> n;
    for (int i = 0; i < n; i++){
        int grad;
        fi >> grad;
        secv_grade.push_back(grad);
        //verificam daca un grad este mai mare decat n, daca da atunci
        //nu se poate construi graful cerut
        if (grad > n - 1){
            fo << "NU";
            return;
        }
        //altfel, adunam gradul la suma pentru a doua conditie necesara
        suma_grade += grad;
    }
    //daca suma gradelor este impara, nu se poate construi graful cerut
    if (suma_grade % 2 != 0){
        fo << "NU";
        return;
    }
    //sortam descrescator vectorul de grade pentru a alege cel mai mare grad
    //din secventa
    sort(secv_grade.begin(), secv_grade.end(), greater<int>());
    //algoritmul ruleaza cat timp secventa contine valori nenule
    //vectorul fiind sortat descrescator, daca primul element este nul,
    //inseamna ca si celelalte elemente sunt nule
    while (secv_grade[0] > 0){
        //scadem gradele pentru atatea noduri cat este gradul nodului curent
        for (int i = 1; i <= secv_grade[0]; i++){
            secv_grade[i]--;
            //daca un nod, prin aceasta scadere, ajunge sa aiba grad negativ,
            //atunci nu se poate construi graful cerut
            if (secv_grade[i] < 0){
                fo << "NU";
                return;
            }
        }
        //eliminam gradul prelucrat
        secv_grade.erase(secv_grade.begin());
        //sortam din nou vectorul descrescator pentru a alege, in continuare,
        //cel mai mare grad din secventa
        sort(secv_grade.begin(), secv_grade.end(), greater<int>());
    }
    //daca programul nu a fost intrerupt pana acum, inseamna ca putem construi
    //graful cerut
    fo << "DA";
}

int main()
{
    Graf g;
    //1. algoritmul BFS
    g.citire_orientat_bfs();
    g.BFS();
    //2. componente conexe (algoritmul DFS)
    //g.citire_neorientat();
    //g.componente_conexe();
    //3. componente biconexe
    /*
    g.citire_neorientat();
    g.componente_biconexe(1, 0, 0); //pornim din nodul 1, care va avea
                                    //adancimea 0
    //afisam numarul de componente biconexe
    int nr_comp = comp_biconexe.size();
    fo << nr_comp << "\n";
    //incepem sa procesam fiecare componenta pentru afisare
    for (int i = 0; i < nr_comp; i++){
        //ordonam crescator nodurile care alcatuiesc componenta
        sort(comp_biconexe[i].begin(), comp_biconexe[i].end());
        //dupa executarea functiei, in comp_biconexe avem muchiile
        //componentelor; trebuie sa lasam doar nodurile in componenta
        comp_biconexe[i].erase(unique(comp_biconexe[i].begin(), comp_biconexe[i].end()), comp_biconexe[i].end());
        //afisam nodurile care alcatuiesc componenta
        int n_noduri = comp_biconexe[i].size();
        for (int j = 0; j < n_noduri; j++){
            fo << comp_biconexe[i][j] << " ";
        }
        fo << "\n";
    }
    */
    //4. componente tare conexe (algoritmul lui Tarjan)
    /*
    g.citire_orientat();
    g.componente_tare_conexe();
    int nr_comp = comp_tare_conexe.size();
    fo << nr_comp << "\n";
    //incepem sa procesam fiecare componenta pentru afisare
    for (int i = 0; i < nr_comp; i++){
        //ordonam crescator nodurile care alcatuiesc componenta
        sort(comp_tare_conexe[i].begin(), comp_tare_conexe[i].end());
        //afisam nodurile care alcatuiesc componenta
        int n_noduri = comp_tare_conexe[i].size();
        for (int j = 0; j < n_noduri; j++){
            fo << comp_tare_conexe[i][j] << " ";
        }
        fo << "\n";
    }
    */
    //5. algoritmul Havel-Hakimi
    //g.havel_hakimi();
    return 0;
}