Cod sursa(job #2797400)

Utilizator Dantelogus99Gruia Gabriel Dantelogus99 Data 9 noiembrie 2021 20:36:31
Problema Parcurgere DFS - componente conexe Scor 15
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 16.6 kb
#include <fstream>
#include <iostream>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;

constexpr auto INF = 2147483647;

bool isDirected;
bool isWeighted;
bool isCappable;

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



struct Edge
{
    int src;
    int dest;
    int cost;
    int cap;

    Edge(int s = 0, int d = 0, int c = 0, int g = 0) : src(s), dest(d), cost(c), cap(g) {}

    friend istream& operator >> (istream& in, Edge& e)
    {
        in >> e.src >> e.dest;
        if (isWeighted) in >> e.cost;
        if (isCappable) in >> e.cap;
            
        return in;
    }

    friend ostream& operator << (ostream& out, Edge& e)
    {
        out << e.src << " ---> " << e.dest << " :  ";
        if (isWeighted) out << e.cost << " (cost)";
        if (isCappable) out << e.cap << " (cap)";
        out << "\n";

        return out;
    }
};


class Graph
{
// Implementation of  << UTILITARY Functions >>  for (UN)Directed, (UN)Weighted & (UN)Cappable << Graphs >>;

#pragma region Data

    int nrNodes;
    int nrEdges;

    vector<list<Edge>> adjacencyList;

#pragma endregion


public:

#pragma region IO_Methods

    Graph(int n = 0, int m = 0) : nrNodes(n), nrEdges(m){}

    void readAdjacencyList()
    {
        adjacencyList.resize(nrNodes + 1);

        for (int i = 0; i < nrEdges; i++)
        {
            Edge edge;
            fin >> edge;
            adjacencyList[edge.src].push_back(edge);
        }
    }

    void printAdjacencyList()
    {
        fout << "\n>   nrNodes = " << nrNodes;
        fout << "\n>   nrEdges = " << nrEdges;

        fout << "\n\n\n>   The ADJACENCY LIST associated to the ";
        isDirected ? fout << "DIRECTED" : fout << "UNDIRECTED";
        isWeighted ? fout << ", WEIGHTED" : fout << ", UNWEIGHTED";
        fout << " graph <G>:";

        for (int i = 1; i <= nrNodes; i++)
            if (adjacencyList[i].size())
            {
                fout << "\n\nNode " << i << ":\n";
                for (auto& edge : adjacencyList[i])
                    fout << edge;
            }
    }

#pragma endregion


#pragma region Unweighted_Graph_Methods

    vector<int> getUnweightedDistances(int startingNode)
    {
        vector<int> distances(nrNodes + 1, -1);

        BFS(startingNode, distances);

        return distances;
    }

    list<set<int>> getConnectedComponents()
    {
        list<set<int>> CClist;
        vector<bool> isVisited(nrNodes + 1, false);

        for (int i = 1; i <= nrNodes; i++) // parcurgem nodurile pana gasim unul nevizitat, indicand o noua componenta conexa, si pornim DFS din acel nod
            if (!isVisited[i])
            {
                set<int> connectedComponent;
                DFS(i, isVisited, connectedComponent);

                CClist.push_back(connectedComponent);
            }

        return CClist;
    }

    list<set<int>> getStronglyConnectedComponents()
    {
        list<set<int>> SCClist;

        vector<int> discoveryOrder(nrNodes + 1, 0);
        vector<int> lowestLink(nrNodes + 1, 0);
        vector<bool> onStack(nrNodes + 1, false);
        stack<int> path;

        for (int i = 1; i <= nrNodes; i++)
            if (!discoveryOrder[i])
                TarjanDFS(i, discoveryOrder, lowestLink, path, onStack, SCClist);

        return SCClist;
    }

    list<set<int>> getBiconnectedComponents()
    {
        list<set<int>> BCClist;

        vector<int> levels(nrNodes + 1, 0);
        vector<int> lowestLink(nrNodes + 1, 0);
        stack<int> path;

        BiconnectedDFS(1, 1, levels, lowestLink, path, BCClist);

        return BCClist;
    }

    list<pair<int, int>> getCriticalEdges()
    {
        list<pair<int, int>> CElist;

        vector<int> discoveryOrder(nrNodes + 1, 0);
        vector<int> lowestLink(nrNodes + 1, 0);

        CriticalEdgesDFS(1, 0, discoveryOrder, lowestLink, CElist);

        return CElist;
    }

    list<int> getTopologicalOrder()
    {
        list<int> TOlist;
        vector<bool> isVisited(nrNodes + 1, false);

        for (int i = 1; i <= nrNodes; i++)
            if (!isVisited[i])
                TopologicalOrderDFS(i, isVisited, TOlist);

        return TOlist;
    }

    bool getValidGraph()
    {
        bool validity;
        vector<int> degrees;

        int deg;
        while (fin >> deg)
        {
            degrees.push_back(deg);
        }

        HavelHakimi(degrees, validity);

        return validity;
    }

#pragma endregion

#pragma region Weighted_Graph_Methods

    vector<int> getWeightedDistances(int startingNode)
    {
        vector<int> distance(nrNodes + 1, INF);

        Dijkstra(startingNode, distance);

        return distance;
    }

    vector<int> getBellmanFordDistances(int startingNode)
    {
        vector<int> distance(nrNodes + 1, INF);

        BellmanFord(startingNode, distance);

        return distance;
    }

#pragma endregion


private:

#pragma region Private_Unweighted_Methods

    void BFS(int startingNode, vector<int>& dist)
    {
        queue<int> inProcessing;

        inProcessing.push(startingNode);
        dist[startingNode] = 0;

        while (!inProcessing.empty())
        {
            int last = inProcessing.front(); // extragem primul nod din coada si ii parcurgem/adaugam vecinii in coada

            inProcessing.pop();

            for (auto& edge : adjacencyList[last]) // vecin.first = nodul din lista de adiacenta a nodului curent
                if (dist[edge.dest] == -1)                // adauga toti vecinii nevizitati ai nodului curent in coada
                {
                    inProcessing.push(edge.dest);
                    dist[edge.dest] = dist[last] + 1;
                }
        }

        dist.erase(dist.begin());
    }

    void DFS(int startingNode, vector<bool>& isVisited, set<int>& connectedComponent)
    {
        isVisited[startingNode] = true;
        connectedComponent.insert(startingNode);

        for (auto& edge : adjacencyList[startingNode]) // vecin.first = nodul din lista de adiacenta a nodului curent
            if (!isVisited[edge.dest])          // parcurgem componenta conexa de care apartine nodul de pornire
                DFS(edge.dest, isVisited, connectedComponent);
    }

    void TarjanDFS(int currentNode, vector<int>& discOrder, vector<int>& lowLink, stack<int>& path, vector<bool>& onStack, list<set<int>>& SCClist)
    {
        static int currentID = 1;
        path.push(currentNode);
        onStack[currentNode] = true;
        discOrder[currentNode] = lowLink[currentNode] = currentID++;

        for (auto& edge : adjacencyList[currentNode]) // vecin.first = nodul din lista de adiacenta a nodului curent
        {
            if (discOrder[edge.dest]) // daca nodul vecin a fost explorat si se afla pe stiva de ordine, il incadram intr-o componenta conexa;
            {
                if (onStack[edge.dest])
                    lowLink[edge.src] = min(lowLink[edge.src], discOrder[edge.dest]);
            }
            else  // daca nodul vecin nu a fost explorat, pornim DFS din el, iar la revenirea din recursie, il incadram intr-o componenta conexa;
            {
                TarjanDFS(edge.dest, discOrder, lowLink, path, onStack, SCClist);

                lowLink[edge.src] = min(lowLink[edge.src], lowLink[edge.dest]);
            }
        }

                                                             // daca ID - ul nodului corespunde cu lowLink - value - ul sau, inseamna ca nodul este radacina componentei sale conexe,
        if (lowLink[currentNode] == discOrder[currentNode])  // si putem scoate de pe stiva toate nodurile pana la startingNode, deoarece am terminat de explorat componenta respectiva;
        {
            set<int> connectedComp;
            int last;

            do
            {
                last = path.top();
                path.pop();
                onStack[last] = false;

                connectedComp.insert(last);
            } while (currentNode != last);

            SCClist.push_back(connectedComp);
        }
    }

    void BiconnectedDFS(int currentNode, int currentLevel, vector<int>& level, vector<int>& lowLink, stack<int>& path, list<set<int>>& BCClist)
    {
        path.push(currentNode); // stiva retine parcurgerea DFS a subarborilor grafului
        level[currentNode] = lowLink[currentNode] = currentLevel; // adancimi[x] = nivelul de adancime al nodului X din arborele DFS;     lowLink[x] = adancimea minima la care se poate intoarce nodul X;

        for (auto& edge : adjacencyList[currentNode])
        {
            if (level[edge.dest]) // daca nodul vecin a fost explorat
            {
                lowLink[edge.src] = min(lowLink[edge.src], level[edge.dest]); // adancimea minima a nodului curent S = min (adancimea sa minima curenta; adancimea vecinilor sai)
            }
            else
            {
                BiconnectedDFS(edge.dest, currentLevel + 1, level, lowLink, path, BCClist);
                lowLink[edge.src] = min(lowLink[edge.src], lowLink[edge.dest]); // la intoarcerea din recursie, nodurile cu adancimea < adancimea nodului pe care s-a facut recursia
                                                                         // isi minimizeaza adancimea minima lowLink cu a succesorilor;
                if (lowLink[edge.dest] == level[edge.src])
                {                                          // cand ajungem la succesorul radacinii componentei, eliminam nodurile pana la radacina din stiva, formand o componenta biconexa;
                    set<int> biconnectedCopm;
                    int last;

                    do
                    {
                        last = path.top();
                        path.pop();

                        biconnectedCopm.insert(last);
                    } while (edge.src != last);

                    path.push(edge.src);

                    BCClist.push_back(biconnectedCopm);
                }
            }
        }
    }

    void CriticalEdgesDFS(int startingNode, int previous, vector<int>& discOrder, vector<int>& lowLink, list<pair<int, int>>& CElist)
    {
        static int currentID = 1;
        discOrder[startingNode] = lowLink[startingNode] = currentID++;

        for (auto& edge : adjacencyList[startingNode])
        {
            if (discOrder[edge.dest])
            {
                if (edge.dest != previous)
                    lowLink[edge.src] = min(lowLink[edge.src], discOrder[edge.dest]);
            }
            else
            {
                CriticalEdgesDFS(edge.dest, edge.src, discOrder, lowLink, CElist);

                lowLink[edge.src] = min(lowLink[edge.src], lowLink[edge.dest]);

                if (lowLink[edge.dest] > discOrder[edge.src])
                    CElist.push_back(make_pair(edge.src, edge.dest));
            }      
        }
    }

    void TopologicalOrderDFS(int startingNode, vector<bool>& isVisited, list<int>& TOlist)
    {
        isVisited[startingNode] = true;

        for (auto& edge : adjacencyList[startingNode])
            if (!isVisited[edge.dest])
                TopologicalOrderDFS(edge.dest, isVisited, TOlist);

        TOlist.push_front(startingNode);
    }

    void HavelHakimi(vector<int>& degrees, bool& valid)
    {
        while (!degrees.empty())
        {
            sort(degrees.begin(), degrees.end(), greater<>());

            if (degrees[0] == 0)
            {
                valid = true;
                break;
            }

            if (degrees[0] > degrees.size() - 1)
            {
                valid = false;
                break;
            }

            for (int i = 1; i <= degrees[0]; i++)
            {
                degrees[i]--;

                if (degrees[i] < 0)
                {
                    valid = false;
                    break;
                }
            }

            degrees.erase(degrees.begin());
        }
    }

#pragma endregion

#pragma region Private_Weighted_Methods

    void Dijkstra(int startingNode, vector<int>& dist)
    {
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> inProcessing;

        inProcessing.push(make_pair(0, startingNode));
        dist[startingNode] = 0;

        while (!inProcessing.empty())
        {
            int nearestNode = inProcessing.top().second;
            int currentDistance = inProcessing.top().first;
            inProcessing.pop();

            if (currentDistance <= dist[nearestNode])
            {
                for (auto& edge : adjacencyList[nearestNode])
                {

                    if (dist[edge.dest] == INF && dist[nearestNode] + edge.cost < dist[edge.dest])
                    {
                        dist[edge.dest] = dist[nearestNode] + edge.cost;
                        inProcessing.push(make_pair(dist[edge.dest], edge.dest));
                    }
                }
            }
        }

        for (auto& d : dist)
            d == INF ? d : 0;
    }

    void BellmanFord(int startingNode, vector<int>& dist)
    {
        queue<int> processingQueue;
        vector<bool> inQueue(nrNodes + 1, false);
        vector<int> countIterations(nrNodes + 1, 0);

        dist[startingNode] = 0;
        processingQueue.push(startingNode);
        inQueue[startingNode] = true;


        while (!processingQueue.empty())
        {
            int currentNode = processingQueue.front();
            processingQueue.pop();
            inQueue[currentNode] = false;

            countIterations[currentNode]++;
            if (countIterations[currentNode] == nrNodes)
            {
                dist[0] = 0;
                break;
            }

            for (auto& edge : adjacencyList[currentNode])
            {
                if (dist[edge.dest] > dist[currentNode] + edge.cost)
                {
                    dist[edge.dest] = dist[currentNode] + edge.cost;

                    if (!inQueue[edge.dest])
                    {
                        processingQueue.push(edge.dest);
                        inQueue[edge.dest] = true;
                    }
                }
            }
        }
    }

#pragma endregion

};



int main()
{
    isDirected = false;
    isWeighted = false;
    isCappable = false;

    int nodes, edges, start;
    fin >> nodes >> edges;

    Graph G(nodes, edges); //G(nodes, edges, isDirected, isWeighted);

    G.readAdjacencyList();
    //G.printAdjacencyList();


#pragma region Public_Methods_Calls

/* ---> MINIMAL DISTANCES (UNWEIGHTED) <--- */

    /*vector<int> minDistances_UW = G.getUnweightedDistances(start);

    for (auto dist : minDistances_UW)
        fout << dist << " ";*/


/* ---> CONNECTED COMPONENTS <--- */

    list<set<int>> CClist = G.getConnectedComponents();

    fout << CClist.size();


/* ---> STRONGLY CONNECTED COMPONENTS <--- */

    /*list<set<int>> SCClist = G.getStronglyConnectedComponents();

    fout << SCClist.size() << "\n";

    for (auto SCC : SCClist)
    {
        for (auto node : SCC)
            fout << node << " ";
        fout << "\n";
    }*/


/* ---> BICONNECTED COMPONENTS <--- */

    /*list<set<int>> BCClist = G.getBiconnectedComponents();

    fout << BCClist.size() << "\n";

    for (auto BCC : BCClist)
    {
        for (auto node : BCC)
            fout << node << " ";
        fout << "\n";
    }*/


/* ---> CRITICAL EDGES <--- */

    /*list<pair<int, int>> CElist = G.getCriticalEdges();

    fout << CElist.size() << "\n";

    for (auto edge : CElist)
        fout << "(" << edge.first << " " << edge.second << ")" << "\n";*/


/* ---> TOPOLOGICAL ORDER <--- */

    /*list<int> TOlist = G.getTopologicalOrder();

    for (auto node : TOlist)
        fout << node << " ";*/


/* ---> GRAPHICAL SEQUENCE <-- - */

    /*bool HH = G.getValidGraph();

    HH ? fout << "Yes" : fout << "No";*/


/* ---> MINIMAL DISTANCES (WEIGHTED) <--- */

    /*vector<int> minDistances_W = G.getWeightedDistances(1);

    for (int i = 2; i <= nodes; i++)
        fout << minDistances_W[i] << " ";*/


/* ---> MINMAL DISTANCES (W/ NEGATIVE COSTS) <--- */

    /*vector<int> minDistances_NegC = G.getBellmanFordDistances(1);

    if (minDistances_NegC[0] == 0)
        fout << "Ciclu negativ!";
    else
        for (int i = 2; i <= nodes; i++)
            fout << minDistances_NegC[i] << " ";*/

#pragma endregion
}