Cod sursa(job #2958297)

Utilizator AndreiAncutaAndrei Ancuta AndreiAncuta Data 25 decembrie 2022 19:50:00
Problema Cuplaj maxim in graf bipartit Scor 50
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 4.56 kb
#include <fstream>
#include <limits>
#include <queue>
#include <vector>

using std::vector;
using Node = int;

const int INF = std::numeric_limits<int>::max();

class FlowNetwork {
private:
    int nodeCount;
    vector<vector<int>> capacityMatrix;
    vector<vector<Node>> adjList;

    void buildBfsTree(Node s, Node t, vector<Node> &prevInTree) {
        std::fill(prevInTree.begin(), prevInTree.end(), -1);
        prevInTree[s] = -2;

        std::queue<Node> bfsQueue;
        bfsQueue.push(s);

        while (!bfsQueue.empty()) {
            auto node = bfsQueue.front();
            bfsQueue.pop();

            for (Node next: adjList[node]) {
                // Skip visited nodes and saturated edges
                if (prevInTree[next] != -1 || capacityMatrix[node][next] == 0)
                    continue;

                if (next == t)
                    continue;

                prevInTree[next] = node;
                bfsQueue.push(next);
            }
        }
    }

    /// Find the minimum capacity through the path from start to end
    int findMinFlowThroughPath(Node start, Node end, vector<Node> &prevInPath) {
        int flow = INF;
        Node current = end;
        while (current != start) {
            Node prevNode = prevInPath[current];
            flow = std::min(flow, capacityMatrix[prevNode][current]);
            current = prevNode;
        }

        return flow;
    }

    /// Update the flows through the path from start to end with the given value
    void updateFlowThroughPath(Node start, Node end, vector<Node> &prevInPath, int flow) {
        Node current = end;
        while (current != start) {
            Node prev = prevInPath[current];
            capacityMatrix[prev][current] -= flow;
            capacityMatrix[current][prev] += flow;
            current = prev;
        }

    }

    /// Try to find an augmenting path and return the added flow
    int tryAugmentFlow(Node s, Node t, vector<Node> &prevInTree) {
        buildBfsTree(s, t, prevInTree);

        int totalAugmentingFlow = 0;

        // For each node just before t, join it with t and use that as an augmenting path
        for (Node nodeBeforeT: adjList[t]) {
            // Skip node if it was unreachable due to saturation
            if (prevInTree[nodeBeforeT] == -1)
                continue;

            // Join nodeBeforeT and t and update the capacities through the path
            prevInTree[t] = nodeBeforeT;
            auto minFlowThroughPath = findMinFlowThroughPath(s, t, prevInTree);
            updateFlowThroughPath(s, t, prevInTree, minFlowThroughPath);

            totalAugmentingFlow += minFlowThroughPath;
        }

        return totalAugmentingFlow;
    }

public:
    struct Edge {
        Node from;
        Node to;
    };

    explicit FlowNetwork(int nodeCount) : nodeCount(nodeCount) {
        capacityMatrix.resize(nodeCount, vector<int>(nodeCount));
        adjList.resize(nodeCount);
    }

    void addEdge(Node from, Node to, int capacity) {
        adjList[from].push_back(to);
        adjList[to].push_back(from);
        capacityMatrix[from][to] = capacity;
    }

    const vector<Node> &getNeighbors(Node from) {
        return adjList[from];
    }

    int computeMaxFlow(Node s, Node t) {
        int maxFlow = 0;
        vector<Node> prevInPath(nodeCount);

        int newFlow;
        while ((newFlow = tryAugmentFlow(s, t, prevInPath)) > 0) {
            maxFlow += newFlow;
        }

        return maxFlow;
    }

    int getEdgeFlow(Node from, Node to) {
        return capacityMatrix[from][to];
    }
};

int main() {
    std::ifstream in("cuplaj.in");

    int fromCount, toCount, edgeCount;
    in >> fromCount >> toCount >> edgeCount;

    int nodeCount = fromCount + toCount + 1;

    FlowNetwork network(nodeCount + 1);

    for (int i = 1; i <= edgeCount; ++i) {
        Node from, to;
        in >> from >> to;
        network.addEdge(from, to + fromCount, 1);
    }

    in.close();

    // Connect s and t to the two partitions
    Node s = 0;
    Node t = nodeCount;
    for (Node from = 1; from <= fromCount; ++from) {
        network.addEdge(s, from, 1);
    }
    for (Node to = 1; to <= toCount; ++to) {
        network.addEdge(to + fromCount, t, 1);
    }

    std::ofstream out("cuplaj.out");
    out << network.computeMaxFlow(s, t) << '\n';

    for (Node from = 1; from <= fromCount; ++from) {
        for (Node to : network.getNeighbors(from)) {
            if (to == s) continue;
            if (network.getEdgeFlow(from, to) == 0) {
                out << from << ' ' << to - fromCount << '\n';
            }
        }
    }

    out.close();

    return 0;
}