/* -*- C++ -*- * src/lemon/max_matching.h - Part of LEMON, a generic C++ optimization library * * Copyright (C) 2004 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport * (Egervary Combinatorial Optimization Research Group, EGRES). * * Permission to use, modify and distribute this software is granted * provided that this copyright notice appears in all copies. For * precise terms see the accompanying LICENSE file. * * This software is provided "AS IS" with no warranty of any kind, * express or implied, and with no claim as to its suitability for any * purpose. * */ #ifndef LEMON_MAX_MATCHING_H #define LEMON_MAX_MATCHING_H #include #include #include #include ///\ingroup galgs ///\file ///\brief Maximum matching algorithm. namespace lemon { /// \addtogroup galgs /// @{ ///Edmonds' alternating forest maximum matching algorithm. ///This class provides Edmonds' alternating forest matching ///algorithm. The starting matching (if any) can be passed to the ///algorithm using read-in functions \ref readNMapNode, \ref ///readNMapEdge or \ref readEMapBool depending on the container. The ///resulting maximum matching can be attained by write-out functions ///\ref writeNMapNode, \ref writeNMapEdge or \ref writeEMapBool ///depending on the preferred container. /// ///The dual side of a matching is a map of the nodes to ///MaxMatching::pos_enum, having values D, A and C showing the ///Gallai-Edmonds decomposition of the graph. The nodes in D induce ///a graph with factor-critical components, the nodes in A form the ///barrier, and the nodes in C induce a graph having a perfect ///matching. This decomposition can be attained by calling \ref ///writePos after running the algorithm. Before subsequent runs, ///the function \ref resetPos() must be called. /// ///\param Graph The undirected graph type the algorithm runs on. /// ///\author Jacint Szabo template class MaxMatching { typedef typename Graph::Node Node; typedef typename Graph::Edge Edge; typedef typename Graph::UndirEdgeIt UndirEdgeIt; typedef typename Graph::NodeIt NodeIt; typedef typename Graph::IncEdgeIt IncEdgeIt; typedef UnionFindEnum UFE; public: ///Indicates the Gallai-Edmonds decomposition of the graph. ///Indicates the Gallai-Edmonds decomposition of the graph, which ///shows an upper bound on the size of a maximum matching. The ///nodes with pos_enum \c D induce a graph with factor-critical ///components, the nodes in \c A form the canonical barrier, and the ///nodes in \c C induce a graph having a perfect matching. enum pos_enum { D=0, A=1, C=2 }; private: static const int HEUR_density=2; const Graph& g; typename Graph::template NodeMap mate; typename Graph::template NodeMap position; public: MaxMatching(const Graph& _g) : g(_g), mate(_g,INVALID), position(_g,C) {} ///Runs Edmonds' algorithm. ///Runs Edmonds' algorithm for sparse graphs (number of edges < ///2*number of nodes), and a heuristical Edmonds' algorithm with a ///heuristic of postponing shrinks for dense graphs. \pre Before ///the subsequent calls \ref resetPos must be called. inline void run(); ///Runs Edmonds' algorithm. ///If heur=0 it runs Edmonds' algorithm. If heur=1 it runs ///Edmonds' algorithm with a heuristic of postponing shrinks, ///giving a faster algorithm for dense graphs. \pre Before the ///subsequent calls \ref resetPos must be called. void runEdmonds( int heur ); ///Finds a greedy matching starting from the actual matching. ///Starting form the actual matching stored, it finds a maximal ///greedy matching. void greedyMatching(); ///Returns the size of the actual matching stored. ///Returns the size of the actual matching stored. After \ref ///run() it returns the size of a maximum matching in the graph. int size() const; ///Resets the map storing the Gallai-Edmonds decomposition. ///Resets the map storing the Gallai-Edmonds decomposition of the ///graph, making it possible to run the algorithm. Must be called ///before all runs of the Edmonds algorithm, except for the first ///run. void resetPos(); ///Resets the actual matching to the empty matching. ///Resets the actual matching to the empty matching. /// void resetMatching(); ///Reads a matching from a \c Node map of \c Nodes. ///Reads a matching from a \c Node map of \c Nodes. This map must be \e ///symmetric, i.e. if \c map[u]==v then \c map[v]==u must hold, and ///\c uv will be an edge of the matching. template void readNMapNode(NMapN& map) { for(NodeIt v(g); v!=INVALID; ++v) { mate.set(v,map[v]); } } ///Writes the stored matching to a \c Node map of \c Nodes. ///Writes the stored matching to a \c Node map of \c Nodes. The ///resulting map will be \e symmetric, i.e. if \c map[u]==v then \c ///map[v]==u will hold, and now \c uv is an edge of the matching. template void writeNMapNode (NMapN& map) const { for(NodeIt v(g); v!=INVALID; ++v) { map.set(v,mate[v]); } } ///Reads a matching from a \c Node map of \c Edges. ///Reads a matching from a \c Node map of incident \c Edges. This ///map must have the property that if \c G.target(map[u])==v then \c ///G.target(map[v])==u must hold, and now this edge is an edge of ///the matching. template void readNMapEdge(NMapE& map) { for(NodeIt v(g); v!=INVALID; ++v) { Edge e=map[v]; if ( g.valid(e) ) g.source(e) == v ? mate.set(v,g.target(e)) : mate.set(v,g.source(e)); } } ///Writes the matching stored to a \c Node map of \c Edges. ///Writes the stored matching to a \c Node map of incident \c ///Edges. This map will have the property that if \c ///g.target(map[u])==v then \c g.target(map[v])==u holds, and now this ///edge is an edge of the matching. template void writeNMapEdge (NMapE& map) const { typename Graph::template NodeMap todo(g,true); for(NodeIt v(g); v!=INVALID; ++v) { if ( todo[v] && mate[v]!=INVALID ) { Node u=mate[v]; for(IncEdgeIt e(g,v); e!=INVALID; ++e) { if ( g.target(e) == u ) { map.set(u,e); map.set(v,e); todo.set(u,false); todo.set(v,false); break; } } } } } ///Reads a matching from an \c Edge map of \c bools. ///Reads a matching from an \c Edge map of \c bools. This map must ///have the property that there are no two adjacent edges \c e, \c ///f with \c map[e]==map[f]==true. The edges \c e with \c ///map[e]==true form the matching. template void readEMapBool(EMapB& map) { for(UndirEdgeIt e(g); e!=INVALID; ++e) { if ( map[e] ) { Node u=g.source(e); Node v=g.target(e); mate.set(u,v); mate.set(v,u); } } } ///Writes the matching stored to an \c Edge map of \c bools. ///Writes the matching stored to an \c Edge map of \c bools. This ///map will have the property that there are no two adjacent edges ///\c e, \c f with \c map[e]==map[f]==true. The edges \c e with \c ///map[e]==true form the matching. template void writeEMapBool (EMapB& map) const { for(UndirEdgeIt e(g); e!=INVALID; ++e) map.set(e,false); typename Graph::template NodeMap todo(g,true); for(NodeIt v(g); v!=INVALID; ++v) { if ( todo[v] && mate[v]!=INVALID ) { Node u=mate[v]; for(IncEdgeIt e(g,v); e!=INVALID; ++e) { if ( g.target(e) == u ) { map.set(e,true); todo.set(u,false); todo.set(v,false); break; } } } } } ///Writes the canonical decomposition of the graph after running ///the algorithm. ///After calling any run methods of the class, and before calling ///\ref resetPos(), it writes the Gallai-Edmonds canonical ///decomposition of the graph. \c map must be a node map ///of \ref pos_enum 's. template void writePos (NMapEnum& map) const { for(NodeIt v(g); v!=INVALID; ++v) map.set(v,position[v]); } private: void lateShrink(Node v, typename Graph::template NodeMap& ear, UFE& blossom, UFE& tree); void normShrink(Node v, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree); bool noShrinkStep(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q); void shrinkStep(Node& top, Node& middle, Node& bottom, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q); void augment(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree); }; // ********************************************************************** // IMPLEMENTATIONS // ********************************************************************** template void MaxMatching::run() { if ( countUndirEdges(g) < HEUR_density*countNodes(g) ) { greedyMatching(); runEdmonds(0); } else runEdmonds(1); } template void MaxMatching::runEdmonds( int heur=1 ) { typename Graph::template NodeMap ear(g,INVALID); //undefined for the base nodes of the blossoms (i.e. for the //representative elements of UFE blossom) and for the nodes in C typename UFE::MapType blossom_base(g); UFE blossom(blossom_base); typename UFE::MapType tree_base(g); UFE tree(tree_base); for(NodeIt v(g); v!=INVALID; ++v) { if ( position[v]==C && mate[v]==INVALID ) { blossom.insert(v); tree.insert(v); position.set(v,D); if ( heur == 1 ) lateShrink( v, ear, blossom, tree ); else normShrink( v, ear, blossom, tree ); } } } template void MaxMatching::lateShrink(Node v, typename Graph::template NodeMap& ear, UFE& blossom, UFE& tree) { std::queue Q; //queue of the totally unscanned nodes Q.push(v); std::queue R; //queue of the nodes which must be scanned for a possible shrink while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); if ( noShrinkStep( x, ear, blossom, tree, Q ) ) return; else R.push(x); } while ( !R.empty() ) { Node x=R.front(); R.pop(); for( IncEdgeIt e(g,x); e!=INVALID ; ++e ) { Node y=g.target(e); if ( position[y] == D && blossom.find(x) != blossom.find(y) ) { //x and y must be in the same tree typename Graph::template NodeMap path(g,false); Node b=blossom.find(x); path.set(b,true); b=mate[b]; while ( b!=INVALID ) { b=blossom.find(ear[b]); path.set(b,true); b=mate[b]; } //going till the root Node top=y; Node middle=blossom.find(top); Node bottom=x; while ( !path[middle] ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); Node base=middle; top=x; middle=blossom.find(top); bottom=y; Node blossom_base=blossom.find(base); while ( middle!=blossom_base ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); blossom.makeRep(base); } // if shrink is needed while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); if ( noShrinkStep(x, ear, blossom, tree, Q) ) return; else R.push(x); } } //for e } // while ( !R.empty() ) } template void MaxMatching::normShrink(Node v, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree) { std::queue Q; //queue of the unscanned nodes Q.push(v); while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); for( IncEdgeIt e(g,x); e!=INVALID; ++e ) { Node y=g.target(e); switch ( position[y] ) { case D: //x and y must be in the same tree if ( blossom.find(x) != blossom.find(y) ) { //shrink typename Graph::template NodeMap path(g,false); Node b=blossom.find(x); path.set(b,true); b=mate[b]; while ( b!=INVALID ) { b=blossom.find(ear[b]); path.set(b,true); b=mate[b]; } //going till the root Node top=y; Node middle=blossom.find(top); Node bottom=x; while ( !path[middle] ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); Node base=middle; top=x; middle=blossom.find(top); bottom=y; Node blossom_base=blossom.find(base); while ( middle!=blossom_base ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); blossom.makeRep(base); } break; case C: if ( mate[y]!=INVALID ) { //grow ear.set(y,x); Node w=mate[y]; blossom.insert(w); position.set(y,A); position.set(w,D); tree.insert(y); tree.insert(w); tree.join(y,blossom.find(x)); tree.join(w,y); Q.push(w); } else { //augment augment(x, ear, blossom, tree); mate.set(x,y); mate.set(y,x); return; } //if break; default: break; } } } } template void MaxMatching::greedyMatching() { for(NodeIt v(g); v!=INVALID; ++v) if ( mate[v]==INVALID ) { for( IncEdgeIt e(g,v); e!=INVALID ; ++e ) { Node y=g.target(e); if ( mate[y]==INVALID && y!=v ) { mate.set(v,y); mate.set(y,v); break; } } } } template int MaxMatching::size() const { int s=0; for(NodeIt v(g); v!=INVALID; ++v) { if ( mate[v]!=INVALID ) { ++s; } } return (int)s/2; } template void MaxMatching::resetPos() { for(NodeIt v(g); v!=INVALID; ++v) position.set(v,C); } template void MaxMatching::resetMatching() { for(NodeIt v(g); v!=INVALID; ++v) mate.set(v,INVALID); } template bool MaxMatching::noShrinkStep(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q) { for( IncEdgeIt e(g,x); e!= INVALID; ++e ) { Node y=g.target(e); if ( position[y]==C ) { if ( mate[y]!=INVALID ) { //grow ear.set(y,x); Node w=mate[y]; blossom.insert(w); position.set(y,A); position.set(w,D); tree.insert(y); tree.insert(w); tree.join(y,blossom.find(x)); tree.join(w,y); Q.push(w); } else { //augment augment(x, ear, blossom, tree); mate.set(x,y); mate.set(y,x); return true; } } } return false; } template void MaxMatching::shrinkStep(Node& top, Node& middle, Node& bottom, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q) { ear.set(top,bottom); Node t=top; while ( t!=middle ) { Node u=mate[t]; t=ear[u]; ear.set(t,u); } bottom=mate[middle]; position.set(bottom,D); Q.push(bottom); top=ear[bottom]; Node oldmiddle=middle; middle=blossom.find(top); tree.erase(bottom); tree.erase(oldmiddle); blossom.insert(bottom); blossom.join(bottom, oldmiddle); blossom.join(top, oldmiddle); } template void MaxMatching::augment(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree) { Node v=mate[x]; while ( v!=INVALID ) { Node u=ear[v]; mate.set(v,u); Node tmp=v; v=mate[u]; mate.set(u,tmp); } typename UFE::ItemIt it; for (tree.first(it,blossom.find(x)); tree.valid(it); tree.next(it)) { if ( position[it] == D ) { typename UFE::ItemIt b_it; for (blossom.first(b_it,it); blossom.valid(b_it); blossom.next(b_it)) { position.set( b_it ,C); } blossom.eraseClass(it); } else position.set( it ,C); } tree.eraseClass(x); } /// @} } //END OF NAMESPACE LEMON #endif //EDMONDS_H