Cod sursa(job #1396031)

Utilizator rootsroots1 roots Data 21 martie 2015 23:00:59
Problema Cuplaj maxim in graf bipartit Scor 0
Compilator cpp Status done
Runda Arhiva educationala Marime 6.09 kb
#include <fstream>
#include <vector>
#include <queue>

#define MAXV 10001

using namespace std;

ifstream in("cuplaj.in");
ofstream out("cuplaj.out");

int L, R, E;
vector <int> G[2 * MAXV];
int match[2 * MAXV];
int father[2 * MAXV];
int dist[2 * MAXV];
vector <int> paths;
int maxMatch = 0;
int root[2 * MAXV];

inline void readAndBuild() {
    in >> L >> R >> E;
    
    int x, y;
    for (int e = 0; e < E; ++ e) {
        in >> x >> y;
        
        G[x].push_back(y + L);
        G[y + L].push_back(x);
    }
}

inline bool bfs() {
    bool foundPath = false;
    
    queue <int> q;
    
    // push in q only the nodes from L
    // the bfs finds all augmenting paths that end up at some unmatched node in R
    for (int node = L; node > 0; -- node) {
        if (match[node] == 0) { // if node is unmatched
            father[node] = node; // it becomes an independent tree
            dist[node] = 0; // its length is 0
            root[node] = node; // the root of the tree is node
            
            q.push(node); // and push it in q for expansion (follow the path)
        } else { // if node is matched (i.e. belongs to an edge in the current M)
            father[node] = -1; // initialisation
            dist[node] = -1; // initialisation
            root[node] = -1; // initialisation
        }
    }
    
    // the unmatched node from R are not pushed into q,
    // but they are marked to be later recognized as end of path
    for (int node = L + R; node > L; -- node) {
        if (match[node] == 0) { // if node is unmatched
            father[node] = 0; // 0 means that node is an unmatched node in R
        } else {
            father[node] = -1; // initialisation
        }
        
        dist[node] = -1; // initialisation
        root[node] = -1; // initialisation
    }
    
    for (int node; !q.empty(); q.pop()) {
        node = q.front();
        
        for (vector <int>::iterator it = G[node].begin(); it != G[node].end(); ++ it) {
            
            // we need to create an alternating path,
            // thus the edges we can follow are dependent on the parity of the distance to node
            if ((match[*it] == node) == dist[node] % 2) {
                if (father[*it] == -1) { // if *it was not expanded
                    father[*it] = node;
                    dist[*it] = dist[node] + 1;
                    root[*it] = root[node];
                    
                    q.push(*it);
                    
                    // if father[*it] != -1 then there are 2 cases
                    // first case father[*it] != 0, which means that *it belongs to some path already
                    // second case father[*it] == 0 which means we found and "end of path" node (in R)
                    // also there is no need to check for cycles
                    // e.g. L = {1, 2}, R = {1, 2}, E = {(1,1), (1,2), (2,1), (2,2)}
                    // why?
                    // say we have a path p, |p| = l and a cycle c which includes p
                    // then |c| > l (there is no equal because p is a path,
                    // to make p a cycle we need at least one more edge)
                    // by the definition of augmenting paths we also know that
                    // start(p) in L and end(p) in R
                    // because bfs finds the shortest paths
                    // p will be encountered before c;
                    // it the following if, *it (end(p)) is not pushed into q
                    // so the path is not followed anymore,
                    // thus the cycle c is never reached
                } else if (father[*it] == 0) {
                    father[*it] = node;
                    dist[*it] = dist[node] + 1;
                    root[*it] = root[node];
                    
                    foundPath = true;
                    
                    // Hopcroft-Karp is to Edmonds-Blossom (on bipartite matching)
                    // as is
                    // Dinic to Edmonds-Karp (on max flow)
                    // instead of augmenting one path with one bfs
                    // we augment a maximal number of paths with the same bfs
                    // (maximal is not the maximum
                    // and it means as many paths as we can)
                    paths.push_back(*it);
                }
            }
        }
    }
    
    return foundPath;
}

inline void bipartiteMatchingWithHopcroftKarp() {
    for (bool existsAugmentingPath = bfs(); existsAugmentingPath; existsAugmentingPath = bfs()) {
        for (vector <int>::iterator it = paths.begin(); it != paths.end(); ++ it) {
            // by some lemma regarding Hopcroft-Karp
            // all the paths to be augmented here are vertex-disjoint
            // however, this implementation does not guarantee this
            // (one more thing to take into consideration is that
            // the length of the paths might be different,
            // but they are all shortest paths to the respective end-node)
            // because of the property that says
            // all vertices in P (=paths) have degree <= 2
            // we know that the paths cannot be vertex-disjoint only at the end nodes (start in L and end in R)
            // for the end node in R, we deal with it in the bfs
            // for the start node in L, we must check that
            // after we have augmented some paths, the start node of the
            // path we are trying to augment now (that is the root) is not matched
            if (match[root[*it]]) continue;
            
            for (int node = *it; node != father[node]; node = father[father[node]]) {
//                if (dist[node] % 2 == 1) {
                    match[node] = father[node];
                    match[father[node]] = node;
                }
            }
            
            maxMatch ++;
        }
        
        paths.clear();
    }
}

inline void printResult() {
    out << maxMatch << '\n';
    for (int i = 1; i <= L; ++ i) {
        if (match[i]) {
            out << i << ' ' << match[i] - L << '\n';
        }
    }
}

int main() {
    readAndBuild();
    bipartiteMatchingWithHopcroftKarp();
    printResult();
    
    return 0;
}