Cod sursa(job #2959301)

Utilizator asparagusNadu Toma asparagus Data 30 decembrie 2022 16:36:28
Problema Cuplaj maxim in graf bipartit Scor 50
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 8.31 kb
#include <bits/stdc++.h>

using namespace std;


// slightly modified BFS
bool BFS(const vector<pair<int, int>> *forwardAdjacencyList, const vector<pair<int, int>> *backwardAdjacencyList,
         const vector<vector<pair<int, int>>> &capacityAndFlow, int *parentOf, int numberOfNodes, int sourceNode,
         int sinkNode) {
    // declaring and initializing 'isVisited' array
    bool isVisited[numberOfNodes];
    for (int i = 0; i < numberOfNodes; i++)
        isVisited[i] = false;

    queue<int> workingQueue;
    // BFS begins from the source node
    workingQueue.push(sourceNode);
    isVisited[sourceNode] = true;


    // BFS stalls when no more nodes can be
    // accessed or when the sink node is reached
    while (not workingQueue.empty()) {
        int currentNode = workingQueue.front();
        workingQueue.pop();

        // traversing adjacent nodes reachable from forward edges
        for (auto adjacentNode: forwardAdjacencyList[currentNode])
            // only edges with non-null residual edges can be traversed
            if (capacityAndFlow[currentNode][adjacentNode.first].first -
                capacityAndFlow[currentNode][adjacentNode.first].second > 0) {
                // sink node reached
                if (adjacentNode.first == sinkNode) {
                    // marking the parent of the sink node accordingly
                    parentOf[sinkNode] = currentNode;
                    return true;
                }
                    // unvisited node reached
                else if (not isVisited[adjacentNode.first]) {
                    // pushed to the queue
                    workingQueue.push(adjacentNode.first);
                    // setting the parent node
                    parentOf[adjacentNode.first] = currentNode;
                    // marked as visited
                    isVisited[adjacentNode.first] = true;
                }
            }

        // traversing adjacent nodes reachable from backward edges;
        // the same exact operations occur
        for (auto adjacentNode: backwardAdjacencyList[currentNode])
            if (capacityAndFlow[currentNode][adjacentNode.first].first -
                capacityAndFlow[currentNode][adjacentNode.first].second > 0) {
                if (adjacentNode.first == sinkNode) {
                    parentOf[sinkNode] = currentNode;
                    return true;
                } else if (not isVisited[adjacentNode.first]) {
                    workingQueue.push(adjacentNode.first);
                    parentOf[adjacentNode.first] = currentNode;
                    isVisited[adjacentNode.first] = true;
                }
            }
    }

    // sink node could not be reached -> augmenting path couldn't be found
    return false;
}



// driver function, based on the Edmonds-Karp algorithm
void getMaxBipartiteMatching(const vector<pair<int, int>> *forwardAdjacencyList, int numberOfNodes, int numberOfNodesLeft,
                             int sourceNode, int sinkNode) {
    // matrix which holds both the capacity and the currently running flow of an edge from node i to node j
    vector<vector<pair<int, int>>> capacityAndFlow(numberOfNodes,
                                                   vector<pair<int, int>>(numberOfNodes, pair<int, int>()));
    // since there can be a bidirectional edge between two nodes in the original graph, it's easiest
    // to maintain a separate adjacency list for backwards edges, not to overwrite the original
    // bidirectional edges with backward edges, if there are any;
    vector<pair<int, int>> backwardAdjacencyList[numberOfNodes];

    // initializing the backward adjacency list and the capacity-flow matrix
    for (int node = 0; node < numberOfNodes; node++)
        for (auto adjacentNode: forwardAdjacencyList[node]) {
            // storing backward edge
            backwardAdjacencyList[adjacentNode.first].emplace_back(node, adjacentNode.second);

            // all pairs in the matrix are initially 0;
            // even though the forward and backwards edges are stored in distinct
            // adjacency lists, both the capacity and flow will be shared between
            // edges with the same direction, but different types (one forward, one
            // backward), since it is the same as keeping them separate;
            // in order to achieve this, their capacities and flows will be summed
            // up from the very beginning;
            // from now on, whether a forward or backward edge with the same direction
            // connecting the same two nodes will be chosen is indifferent;

            // adding the capacity of the forward edge
            capacityAndFlow[node][adjacentNode.first].first += adjacentNode.second;
            // the flow of the forward edge is initially 0, therefore it was omitted

            // adding the capacity of the backward edge
            capacityAndFlow[adjacentNode.first][node].first += adjacentNode.second;
            // adding the flow of the backward edge;
            // since the residual capacity is the total capacity - the current flow,
            // the backward edge is essentially saturated, so it can't be traversed
            // anymore;
            capacityAndFlow[adjacentNode.first][node].second += adjacentNode.second;
        }


    int parentOf[numberOfNodes], maxFlow = 0;
    // algorithm stalls when no more augmenting paths exist
    while (BFS(forwardAdjacencyList, backwardAdjacencyList, capacityAndFlow, parentOf, numberOfNodes, sourceNode, sinkNode)) {
        // initializing current flow through the found path
        int currentFlowUnits = INT_MAX;

        // first path reconstruction based on 'parentOf' array;
        // computing the minimum flow units which can be pushed
        // through the augmenting path
        for (int childNode = sinkNode; childNode != sourceNode; childNode = parentOf[childNode]) {
            int parentNode = parentOf[childNode];

            // residual capacity = total capacity - current flow
            if (capacityAndFlow[parentNode][childNode].first - capacityAndFlow[parentNode][childNode].second <
                currentFlowUnits)
                currentFlowUnits =
                        capacityAndFlow[parentNode][childNode].first - capacityAndFlow[parentNode][childNode].second;
        }

        // second path reconstruction -> updating the flow of every edge of the augmenting path
        for (int childNode = sinkNode; childNode != sourceNode; childNode = parentOf[childNode]) {
            int parentNode = parentOf[childNode];

            // residual capacity of forward edge + residual capacity of backward edge = total capacity;
            // whenever the flow of one of the edges is modified, the other must be updated accordingly;
            // adding flow units to the chosen edge
            capacityAndFlow[parentNode][childNode].second += currentFlowUnits;
            // removing the same amount of flow units from the opposite edge
            capacityAndFlow[childNode][parentNode].second -= currentFlowUnits;
        }

        maxFlow += currentFlowUnits;
    }

    ofstream output("cuplaj.out");

    output << maxFlow<<'\n';

    for (int nodeInLeft = 1; nodeInLeft <= numberOfNodesLeft; nodeInLeft++)
        for (auto nodeInRight : forwardAdjacencyList[nodeInLeft])
            if (capacityAndFlow[nodeInLeft][nodeInRight.first].second == 1)
                output<<nodeInLeft<<' '<<nodeInRight.first - numberOfNodesLeft<<'\n';

    output.close();
}


int main() {
    ifstream input("cuplaj.in");

    int numberOfNodesLeft, numberOfNodesRight, numberOfEdges;
    input >> numberOfNodesLeft >> numberOfNodesRight >> numberOfEdges;
    int numberOfNodes = numberOfNodesRight + numberOfNodesLeft + 2;

    vector<pair<int, int>> adjacencyList[numberOfNodes];
    int parentNode, childNode;
    for (int i = 0; i < numberOfEdges; i++) {
        input >> parentNode >> childNode;
        childNode=numberOfNodesLeft+childNode;

        adjacencyList[parentNode].emplace_back(childNode, 1);
    }

    input.close();

    for (int nodeInLeft = 1 ; nodeInLeft <= numberOfNodesLeft; nodeInLeft++)
        adjacencyList[0].emplace_back(nodeInLeft, 1);

    for (int nodeInRight = numberOfNodesLeft + 1; nodeInRight <= numberOfNodes-2; nodeInRight++)
        adjacencyList[nodeInRight].emplace_back(numberOfNodes-1, 1);

    getMaxBipartiteMatching(adjacencyList, numberOfNodes, numberOfNodesLeft, 0, numberOfNodes - 1);

    return 0;
}