Cod sursa(job #2968688)

Utilizator ArseniuVictorStefanArseniu Victor Stefan ArseniuVictorStefan Data 21 ianuarie 2023 19:49:03
Problema Cuplaj maxim in graf bipartit Scor 30
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.81 kb
#include <stdio.h>

// represents no node, no edge, no value etc.
#define NONE (-1)

// represents infinity
#define INF (0x3f3f3f3f)

// the maximum dimensions of the graph
#define NMAX (10'000)
#define MMAX (10'000)
#define EMAX (100'000)

// the graph
struct
{
    // the edges of the graph
    struct
    {
        int v; // the node this edge leads to
        bool maximal; // whether or not the edge is in the maximal cardinality matching
        int next; // the next edge
    } edges[2 * EMAX];

    // the first edge of each node
    int begin[NMAX + MMAX];

    // the depths of the nodes
    int depth[NMAX + MMAX];

    // for each node, whether or not it is free
    int free[NMAX + MMAX];

    // the no. of nodes and no. of edges
    int n, m, e;
    
    // init the graph with _n nodes
    void init(int _n, int _m)
    {
        n = _n; m = _m;
        for(int u = 0; u < n + m; u++)
        {
            begin[u] = NONE;
            free[u] = true;
        }
    }

    // adds an edge from u to v that is in the maximal cardinality matching if maximal
    void add_edge(int u, int v, bool maximal)
    {
        edges[e] = { v, maximal, begin[u] };
        begin[u] = e++;
    }

    // BFS queue
    int queue[NMAX + MMAX];
    int qtop;

    // BFS on unmatched edges to calculate the depths; returns true if at least one augmenting path was found
    bool bfs()
    {
        // init the queue
        qtop = 0;
        for(int u = 0; u < n; u++)
            if(free[u])
            {
                // if u is free, add it as starting vertex
                depth[u] = 0;
                queue[qtop++] = u;
            }
            else // else, set its depth to infinity 
                depth[u] = INF;
        
        // set the depths to all nodes in V to infinity
        for(int u = 0; u < m; u++)
            depth[u + n] = INF;
        
        // the depth of the shortest augmenting path
        int aug = INF;

        // while the queue isn't empty
        for(int qfront = 0; qfront < qtop; qfront++)
        {
            // the current vertex
            int u = queue[qfront];

            // ignore if it is at the depth of the shortest augmenting path
            if(depth[u] == aug)
                continue;

            // go through the neighbours
            for(int i = begin[u]; i != NONE; i = edges[i].next)
            {
                int v = edges[i].v; // the next node
                bool maximal = edges[i].maximal; // whether or not the edge is maximal

                // if the edge is not maximal, go through it
                if(!maximal && depth[v] == INF)
                {
                    depth[v] = depth[u] + 1;
                    queue[qtop++] = v;

                    // if we've found a free vertex, set the depth of the augmenting path
                    if(free[v])
                        aug = depth[v];
                }
            }
        }

        // we've found augmenting paths if aug != INF
        return aug != INF;
    }

    // DFS from u to find the augmenting paths and add them to the maximal matching
    bool dfs(int u)
    {
        // base case; u is free so set it to not free and return
        if(free[u])
        {
            free[u] = false;
            depth[u] = INF;
            return true;
        }

        // go through the neighbours and check DFS
        for(int i = begin[u]; i != NONE; i = edges[i].next)
        {
            int v = edges[i].v;

            // if the depth is increasing by 1
            if(depth[v] == depth[u] + 1)
            {
                // recursive call
                if(dfs(v))
                {
                    // add the edge to matching, set u's depth to infinity and return
                    edges[i].maximal = !edges[i].maximal;
                    edges[i ^ 1].maximal = !edges[i ^ 1].maximal;
                    depth[u] = INF;
                    return true;
                }
            }
        }

        // set u's depth to infinity and return
        depth[u] = INF;
        return false;
    }

    // Hopcroft-Karp to find the maximal cardinality matching
    int hopcroft_karp()
    {
        // the size of the matching
        int matching = 0;

        // where there are augmenting paths
        while(bfs())
        {
            // go through the free nodes in U
            for(int u = 0; u < n; u++)
                if(free[u])
                {
                    // mark it as not free and try to find path
                    free[u] = false;
                    if(dfs(u))
                    {
                        // if a path was found, count it
                        matching = matching + 1;
                    }
                    else // else, set u back to free
                        free[u] = true;
                }
        }

        // return the answer
        return matching;
    }
} graph;

int main()
{
    // open the files
    freopen("cuplaj.in", "r", stdin);
    freopen("cuplaj.out", "w", stdout);

    // read the graph
    int n, m, e;
    scanf("%d %d %d", &n, &m, &e);

    // init the graph
    graph.init(n, m);

    // read the edges
    for(int i = 0; i < e; i++)
    {
        int u, v;
        scanf("%d %d", &u, &v);
        u--; v--;

        graph.add_edge(u, v + n, false);
        graph.add_edge(v + n, u, true);
    }

    // print the maximal cardinality matching size
    printf("%d\n", graph.hopcroft_karp());

    // print the edges in the matching
    for(int u = 0; u < n; u++)
        for(int i = graph.begin[u]; i != NONE; i = graph.edges[i].next)
            if(graph.edges[i].maximal)
                printf("%d %d\n", u + 1, graph.edges[i].v - n + 1);

    // exit
    return 0;
}