二分图匹配

二分图匹配

       给定一个二分图G(无向图),在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配.

       选择这样的边数最大的子集称为图的最大匹配问题maximal matchingproblem)

       如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。

       如果该二分图的每条边都有一个权值且存在完备匹配,那么我们要找出一个所有边权值和最大的完备匹配的问题叫做二分图的最优匹配问题

       (在下面给出了求二分图最大匹配和最优匹配的模板代码)

       二分图的最小覆盖数:在二分图中选取最少数目的点集,使得二分图任意一边都至少有一个端点在该点集中。这个点集的大小是二分图的最小覆盖数,且二分图的最小覆盖数==二分图的最大匹配数。

       二分图的最大独立集:在二分图中选取最多数目的点集,使得该点集中的任意两点在二分图中都不存在一条边相连。这个点集就是二分图的最大独立集。且二分图的最大独立集大小==|G|(二分图顶点数) - 二分图最大匹配数。

       DAG的最小路径覆盖:即在DAG图中寻找尽量少的路径,使得每个节点恰好在一条路径上(不同的路径不可能有公共点)。注意:单独的节点也可以作为一条路径。

       DAG最小路径覆盖解法如下:把所有节点i拆为左边点集的i和右边点集的i’,如果DAG图中有ij的有向边,那么添加一条二分图的ij’的无向边。最终DAG的最小路径覆盖数==DAG图的节点数n - 新二分图的最大匹配数m。注意:该由原DAG图构建的新二分图的最大匹配数m<=n-1(想想为什么)

        (具体分析可见刘汝佳<<训练指南>>P357)这里我补充一些内容:比如DAG图的最小路径覆盖为1,那么表示DAG中有1条路径包含了正好n个点,那么对应二分图中有一个n-1边的匹配。因为DAG的路径中只有那条路径的终点是无法匹配的,其他所有节点都在二分图中有匹配边。

       所以如果DAG的最小路径覆盖为x,那么说明有x个点是无法匹配的。那么二分图的匹配数为n-x。综上所述:DAG节点数目n-二分图匹配数m==DAG最小路径覆盖数

       有向图是否存在有向环覆盖?把有向图的所有节点i拆为左边点集的i和右边点集的i’,如果有向图中有ij的有向边,那么添加一条二分图的ij’的无向边。最终如果新二分图的最大匹配数m==有向图的节点数n,那么说明该有向图的所有节点能被正好1个或多个不相交(没有公共节点)的有向环覆盖。

       原理类似于DAG的最小路径覆盖的解释,因为每个节点都能找到一个后继节点继续往下一直走,所以必然原来有向图存在环。又因为在一个可行的最大匹配中,每个节点只有一个后继,所以必然存在不相交的有向环覆盖。

       有向图的最优有向环覆盖:在有向图中找到1个或多个点不想交的环,这些环正好覆盖了有向图的所有节点且这些环上边的权值最大。本问题解法:把有向图的所有节点i拆为左边点集的i和右边点集的i’,如果有向图中有ij的有向边,那么添加一条二分图的ij’的无向边。最终计算二分图的最优完美匹配即可,该二分图的最优完美匹配的权值和就是有向图的最优有向环覆盖的权值和。

 

下面是求二分图最大匹配的模板

//二分图最大匹配模板,二分图都是无向图
//调用下面算法前,保证本图是二分图
/*************vecotr模板*****************/
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=100+5;

struct Max_Match
{
    int n,m;//左右点集大小,点从1开始编号
    vector<int> g[maxn];//g[i]表示左边第i个点邻接的右边点的集合
    bool vis[maxn];//vis[i]表示右边第i个点是否在本次match中被访问过
    int left[maxn];//left[i]==j表右边第i个点与左边第j个点匹配,为-1表无点匹配

    void init(int n,int m)
    {
        this->n=n;
        this->m=m;
        for(int i=1;i<=n;i++) g[i].clear();
        memset(left,-1,sizeof(left));
    }

    //判断从左u点是否可以找到一条增广路
    bool match(int u)
    {
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i];
            if(!vis[v])
            {
                vis[v]=true;
                if(left[v]==-1 || match(left[v]))//找到增广路
                {
                    left[v]=u;
                    return true;
                }
            }
        }
        return false;
    }

    //返回当前二分图的最大匹配数
    int solve()
    {
        int ans=0;//最大匹配数
        for(int i=1;i<=n;i++)//每个左边的节点找一次增广路
        {
            memset(vis,0,sizeof(vis));
            if(match(i)) ans++;//找到一条增广路,形成一个新匹配
        }
        return ans;
    }
}MM;
/*************vecotr模板*****************/

下面是二分图最优完美匹配模板

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100+5;

//把W[maxn][maxn]数组的内容读进去之后,调用solve(n)即可计算出二分图最优匹配
//不过需要保证该图肯定有完美匹配
//因为本图用的W[][]来表示一个完全图,所以一定存在完美匹配的
struct Max_Match
{
    int W[maxn][maxn],n;  //W是权值矩阵,n为左右点集大小
    int Lx[maxn],Ly[maxn];//左右点集的可行顶标值
    bool S[maxn],T[maxn]; //标记左右点集是否已被访问过
    int left[maxn];       //left[i]=j表右i与左j匹配,为-1时表无匹配

    bool match(int i)
    {
        S[i]=true;
        for(int j=1;j<=n;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j])
        {
            T[j]=true;
            if(left[j]==-1 || match(left[j]))
            {
                left[j]=i;
                return true;
            }
        }
        return false;
    }

    //更新可行顶标,纳入更多的边进来
    void update()
    {
        int a=1<<30;
        for(int i=1;i<=n;i++)if(S[i])
        for(int j=1;j<=n;j++)if(!T[j])
        {
            a = min(a,Lx[i]+Ly[j]-W[i][j]);
        }
        for(int i=1;i<=n;i++)
        {
            if(S[i]) Lx[i]-=a;
            if(T[i]) Ly[i]+=a;
        }
    }

    int solve(int n)
    {
        this->n=n;
        memset(left,-1,sizeof(left));
        for(int i=1;i<=n;i++)//初始化可行顶标值
        {
            Lx[i]=Ly[i]=0;
            for(int j=1;j<=n;j++)
                Lx[i]=max(Lx[i], W[i][j]);
        }

        for(int i=1;i<=n;i++)
        {
            while(true)
            {
                for(int j=1;j<=n;j++) S[j]=T[j]=false;
                if(match(i)) break;
                else update();
            }
        }

        int ans=0;//最优完美匹配的权值
        for(int i=1;i<=n;i++) ans+= W[left[i]][i];
        return ans;
    }
}KM;

二分图最大匹配问题

POJ 1469COURSES(二分图最大匹配):解题报告!

POJ 2446Chessboard(二分图最大匹配):棋盘问题。解题报告!

POJ 1274The Perfect Stall(二分图最大匹配):解题报告!

POJ 1325Machine Schedule(最小覆盖数):解题报告!

POJ 3692Kindergarten(最大独立集):解题报告!

POJ 2239Selecting Courses(二分图最大匹配):解题报告!

POJ 2536Gopher II(二分图最大匹配):解题报告!

POJ 3041Asteroids(最小覆盖数):解题报告!

POJ 1466Girls and Boys(二分图最大独立集):解题报告!

POJ 1486Sorting Slides(二分图最大匹配:关键边):解题报告!

POJ 2771Guardian of Decency(二分图最大独立集):解题报告!

HDU 1281棋盘游戏(二分图最大匹配:关键边):解题报告!

HDU 2063过山车(二分图最大匹配:简单题):解题报告!

HDU 2444The Accomodation of Students(二分图判定+最大匹配):解题报告!

HDU 3729I'm Telling the Truth(二分图最大匹配):解题报告!

HDU 3829Cat VS Dog(二分图最大独立集):解题报告!

HDU 149850 years, 50 colors(二分图最小覆盖数):解题报告!

HDU 1845Jimmy’s Assignment(二分图匹配):解题报告!

HDU 4619Warm up 2 (二分图最小覆盖集): 解题报告!

HDU 4185Oil Skimming(二分图最大匹配):解题报告!

HDU 1507Uncle Tom's Inherited Land*(二分图最大匹配:输出一组解):解题报告!

POJ 3020Antenna Placement(二分图的最大匹配):解题报告!

POJ 1422Air Raid(DAG最小路径覆盖):解题报告!

POJ 3216Repairing Company(FLOYD+DAG最小路径覆盖):解题报告!

POJ 2724Purifying Machine(二分图最大匹配):解题报告!

POJ 1548Robots(DAG最小路径覆盖):解题报告!

HDU 4160Dolls(DAG最小路径覆盖):解题报告!

HDU 1054Strategic Game(二分图最小覆盖集):解题报告!

HDU 3991Harry Potter and the Present II(Floyd+DAG最小路径覆盖):解题报告!

POJ 2195Going Home(二分图最优匹配):解题报告!

HDU 2853Assignment(二分图最优匹配:优先用原匹配边):解题报告!

HDU 3315My Brute(二分图最优匹配:优先用原匹配边):解题报告!

HDU 2255奔小康赚大钱(二分图最优匹配:模板题):解题报告!

HDU 2426Interesting Housing Problem(二分图最优匹配):解题报告!

HDU 3718Similarity(二分图最优匹配):解题报告!

HDU 1853Cyclic Tour(二分图最优匹配:有向环覆盖):解题报告!

HDU 2119Matrix(二分图最小边覆盖):解题报告!

HDU 1528Card Game Cheater(二分图最大匹配):解题报告!

HDU 3722Card Game(二分图最优匹配):解题报告!

HDU 3395Special Fish(二分图中最优匹配):解题报告!

HDU 2448Mining Station on the Sea(Floyd+最优匹配):解题报告!

HDU 2282Chocolate(二分图最优匹配):解题报告!

HDU 3488Tour(二分图最优匹配:有向环覆盖):解题报告!

HDU 3435A new Graph Game(二分图最优匹配:有向环覆盖):解题报告!

 

 

 

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值