每日一侃----二分图最大匹配(匈牙利算法)

二分图的模样 :

乱侃一通   :从字面上来理解,二分图确定是两部分,既然是两部分那么这两部分确定各自独立,而后经过必定
             的关系进行创建链接。
 理论上来讲 : 若是一张无向图的 N 个节点(N >= 2) 能够被分红 A,B 两个非空集合,其中 A 交 B = 空集,
             而且在同一集合内的点之间都没有边相连,那么这张无向图就是一张二分图,A,B分别称为二分图
             的左部和右部。

看个图:

根据上图,咱们来侃侃一些概念 :

匹配 : “任意两条边都没有公共端点” 的边的集合被称为图的一组匹配(图中的1 - 6,,2 - 7 )。
最大匹配:包含边数最多的一组被称为二分图的最大匹配 。 (上图的匹配就是最大匹配)

增广路径 : 该算法也被称为增广路算法,每次从左部的点找与右部匹配的点时,咱们须要去找一条增广路径,若是
          该路径存在,说明能够将目前的全部点进行匹配,反之则右部则没有与之左部相匹配的点。
           具体讲解可参考这位大佬的博客,解释的比较形象:https://blog.csdn.net/dark_scope/article/details/8880547

Code PK Self

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int SIZE = 1e5 + 10;
int match[SIZE],vis[SIZE];                    // match : 存储两个部分之间的匹配关系 vis : 标记是否访问过某个点
int head[SIZE],ver[SIZE],Next[SIZE];                // 存储两个部分之间的关系

int n1,n2,m,tot;
int u,v;
 
int main(void) {
    void add(int u,int v);
    bool DFS(int u);
    scanf("%d%d%d",&n1,&n2,&m);
    for(int i = 1; i <= m; i ++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    int res = 0;
    for(int i = 1; i <= n1; i ++) {
        memset(vis,0,sizeof(vis)); // 每次都是从左部一个新的点出发,因此每次都须要进行Clear,咱们只须要关注 match 数组便可
        if(DFS(i)) res ++;         // 左部和右部能够匹配的数量 + 1
    }
    cout << res << endl;
    return 0;
}

void add(int u,int v) {
    ver[ ++ tot] = v,Next[tot] = head[u],head[u] = tot;
    return ;
}

bool DFS(int u) {
    for(int i = head[u]; i; i = Next[i]) {
        int y = ver[i];
        if(!vis[y]) {
            vis[y] = 1;
            if(!match[y] || DFS(match[y])) {  
                           // 有能够直接匹配的或者经过找到一条增广路径的匹配点均可以
                match[y] = u;  
                           // 深搜回溯是,正好把路径上的状态取反,就会将因此的边都匹配
                return true;
            }
        }
    }
    return false;     // 与本身相关联的点都已经名花有主了,说明这条路行不通了
}

有不懂得地方,欢迎留言!!!

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息