[UOJ#79]-一般图最大匹配-带花树

说在前面

嘛…其实也不是很难的
不过需要自己想想细节


题目

UOJ#79传送门
看题可戳传送门


解法

me也不打算写解法,总不能把带花树讲一遍
具体的可以参考2015国家集训队论文:长郡中学陈胤伯《浅谈图的匹配算法及其应用》
里面内容还挺实用的

这里mark一些细节,日后复习用
当然也希望能够帮到对这些问题有疑惑的人qwq

  • BFS是在图上搜索的,因此带花树的一个分支必定对应了一个连通块
    如果带花树有多个分支,那么说明当前未匹配点是一个割点,各个分支就是去掉未匹配点之后的连通块
  • 然后带花树的环可能有多个,可能会出现两个已经被缩的环通过边相连,然后再被缩环的的情况(取决于枚举边的顺序),因此需要用并查集来维护(目的是为了保证一个点的从属(father),与代表当前点的花根一致)
  • 在把环非显式缩点后,新点的表现就是那个花根,所以在 找花根 的过程中,需要调用并查集find函数
  • 维护前驱指针(pre)是为了方便回溯改匹配的过程,把环缩成点之后,前驱指针类似双向链表,这样无论从哪个方向入环,都可以找到一条「未匹配–匹配–未匹配– … –花根」的路径,从而完成回溯改匹配的过程
  • 找花根 的时候跳点需要调用 并查集find函数,而在 缩环 的时候不能调用,因为这里需要把环上的前驱指针改成双向的,如果直接跳,花根的前驱指针就直接指向 match[跳之前的点] 了。另外,环上与花根相连的两个点的前驱指针均是单向的,这一步可能会把单向改成双向的(即花根变化)

下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , head[505] , tp ;
struct Path{
    int pre , to ;
} p[250000] ;

void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
    p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}

int mat[505] , pre[505] , typ[505] , ans ;
int que[505] , fr , ba , fa[505] , tim[505] , tim_c ;
int find( int x ){
    if( fa[x] == x ) return x ;
    return fa[x] = find( fa[x] ) ;
}

int getLca( int u , int v ){
    tim_c ++ , u = find( u ) , v = find( v ) ;
    while( true ){
        if( u ){
            if( tim[u] == tim_c ) return u ;
            tim[u] = tim_c , u = find( pre[mat[u]] ) ;
        } swap( u , v ) ;
    }
}

void blossom( int u , int v , int lca ){
    while( find( u ) != lca ){
        pre[u] = v , fa[u] = fa[ mat[u] ] = lca ;
        v = mat[u] , u = pre[v] ;
        if( typ[v] == 1 ) typ[v] = 0 , que[++ba] = v ;
    }
}


bool BFS( int St ){
    for( int i = 1 ; i <= N ; i ++ )
        fa[i] = i , typ[i] = -1 ;
    typ[St] = 0 , fr = 1 , que[ ba = 1 ] = St ;

    while( ba >= fr ){
        int u = que[fr++] ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( typ[v] == -1 ){
                typ[v] = 1 , pre[v] = u ;
                if( !mat[v] ){
                    for( ; v ; u = pre[v] )
                        mat[v] = u , swap( mat[u] , v ) ;
                    return true ;
                } else typ[ mat[v] ] = 0 , que[++ba] = mat[v] ;
            } else if( !typ[v] && find( u ) != find( v ) ){
                int Lca = getLca( u , v ) ;
                blossom( u , v , Lca ) ;
                blossom( v , u , Lca ) ;
            }
        }
    } return false ;
}

void solve(){
    for( int i = 1 ; i <= N ; i ++ )
        if( !mat[i] ) ans += BFS( i ) ;
    printf( "%d\n" , ans ) ;
    for( int i = 1 ; i <= N ; i ++ )
        printf( "%d " , mat[i] ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 , u , v ; i <= M ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        In( u , v ) ;
    } solve() ;
}

猜你喜欢

转载自blog.csdn.net/Izumi_Hanako/article/details/80346126