二分图最大匹配基本模板

最小顶点覆盖==二分图的最大匹配
最少路径覆盖==n—最大匹配////n为左右集合中的顶点个数
最大点独立集 = 顶点共个数 - 匹配顶点数

多个环的并
    例如:
有n个国家,国家之间通过有向边相连,边有权值,
    现在让你把所有的国家都划成一个一个的圈,使得所有圈的总权值和最大。
    二分图的实质就是多个环,该题就是直接求出的二分图的最大权匹配就能求出最终结果。
    二分图的多个环的并就是二分图的最大(小)权匹配。
    所以KM求得最大匹配就是结果
////////
下面给出关于二分图最大匹配的两个定理
1:最大匹配数 + 最大独立集 = n + m
2:二分图的最小覆盖数 = 最大匹配数
3:最小路径覆盖 = 最大独立集
最大独立集是指求一个二分图中最大的一个点集,该点集内的点互不相连。
最小顶点覆盖是指 在二分图中,用最少的点,让所有的边至少和一个点有关联。
最小路径覆盖是指一个不含圈的有向图G中,G的一个路径覆盖是一个其结点不相交的 路径集合P,图中的每一个结点仅包含于P中的某一条路径。路径可以从任意结点
开始和结束,且长度也为任意值,包括0
二分图的多重匹配

二分图的多重匹配
const int maxn = 100005;
const int maxm = 15;
int cap[maxm];
int Link[maxm][maxn];
int vLink[maxm];
int mat[maxn][maxm];
int vis[maxm];
int n, m;
bool Find(int u)
{
    for(int i = 1; i <= m; i++)
    {
        if(!vis[i] && mat[u][i])
        {
            int v = i;
            vis[v] = 1;
            if(vLink[v] < cap[v])
            {
                Link[v][vLink[v]++] = u;
                return true;
            }
            for(int j = 0; j < vLink[v]; j++)
            {
                if(Find(Link[v][j]))
                {
                    Link[v][j] = u;
                    return true;
                }
            }
        }
    }
    return false;
}

bool solve()
{
    memset(Link, 0, sizeof(Link));
    memset(vLink, 0, sizeof(vLink));
    for(int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof(vis));
        if(!Find(i)) 
            return false;
    }
    return true;
}

int main()
{
    while(EOF != scanf("%d %d",&n, &m))
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                scanf("%d",&mat[i][j]);
            }
        }
        for(int i = 1; i <= m; i++)
        {
            scanf("%d",&cap[i]);
        }
        if(solve()) puts("YES");
        else puts("NO");
    }
    return 0;
}

二分图最大匹配模板

#include<stdio.h>
#include<string.h>
const int maxn = 105;
const int INF = 1000000000;
bool vis[maxn];     //查询右集合中的点有没有被访问过
int link[maxn];     //link[i]表示右集合中的i点是由左集合中的哪个点连接的
int G[maxn][maxn];     //邻接矩阵
int x_cnt;
int y_cnt;            //左右集合的点的个数
bool find(int u)         //用来寻找增广路
{
    for(int i = 1; i <= y_cnt; i++)   //遍历右集合中的每个点
    {
        if(!vis[i] && G[u][i])    //没有被访问过并且和u点有边相连
        {
            vis[i] = true;  //标记该点
            if(link[i] == -1 || find(link[i]))
            {
                //该点是增广路的末端或者是通过这个点可以找到一条增广路
                link[i] = u;//更新增广路   奇偶倒置
                return true;//表示找到一条增广路
            }
        }
    }
    return false;//如果查找了右集合里的所有点还没找到通过该点出发的增广路,该点变不存在增广路
}

int solve()
{
    int num = 0;
    memset(link, -1, sizeof(link));//初始化为-1表示  不与左集合中的任何元素有link
    for(int i = 1; i <= x_cnt; i++)  //遍历左集合
    {
        memset(vis, false, sizeof(vis));//每一次都需要清除标记
        if(find(i))
            num++;//找到一条增广路便num++
    }
    return num;
}
int main()
{
    while(scanf("%d%d",&x_cnt,&y_cnt)!=EOF){
        memset(G,0,sizeof(G));
        int n;
        int x,y;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {scanf("%d%d",&x,&y);
        G[x][y]=1;
        }
        printf("%d
",solve());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/13224ACMer/p/4663756.html