zoukankan      html  css  js  c++  java
  • 「知识学习」二分图的最大匹配、完美匹配和匈牙利算法(HDU-2063)

    定义

    如果一个图((E,V))的顶点集(E)能够被能够被分成两个不相交的集合(X,Y),且每一条边都恰连接(X,Y)中的各一个顶点,那么这个图就是一个二分图
    容易得知,它就是不含有奇数环的图(这个等价定义有时候很重要)。
    一个匹配是一个边的集合,其中任意两条边都没有公共顶点。顾名思义可以得到一个图的最大匹配的定义。特别地,如果一个图的某个匹配中,所有顶点都是匹配点,那么它是一个完美匹配
    由完美匹配和最大匹配这两个定义我们可以得到两类问题:

    1. 有没有可能使得所有顶点都被匹配?
    2. 一个图中最多有多少个顶点参与了匹配?

    求解最大匹配:匈牙利算法

    对于一个正在求匹配的图,我们把依次经过非匹配边、匹配边、非匹配边、……形成的路径叫交替路;而如果一个未匹配点走交替路到了另外一个未匹配点,那么这条交替路成为增广路
    仔细思考一下,就会发现增广路的一个特点:非匹配边比匹配边多一条。这有什么意义呢?当我们把增广路中匹配边、非匹配边的性质交换后,匹配的性质不变,但是匹配边多了一条——我们改进了匹配。
    因此,我们可以通过不停的找增广路来增加匹配中的匹配边和匹配点。然后,根据增广路定理,一个图找不到增广路时,就是它达到最大匹配的时候。
    下面简单的说一下匈牙利算法:

    1. 从二分节点后的左边第1个顶点开始,挑选未匹配点进行搜索,寻找增广路。
      a. 如果经过一个未匹配点,说明寻找成功。更新路径信息,匹配边数+1,停止搜索。
      b. 如果一直没有找到增广路,则不再从这个点开始搜索。事实上,此时搜索后会形成一棵匈牙利树。我们可以永久性地把它从图中删去,而不影响结果。
    2. 由于找到增广路之后需要沿着路径更新匹配,所以我们需要一个结构来记录路径上的点。DFS 版本通过函数调用隐式地使用一个栈,而 BFS 版本使用pre数组。
      上面提到了匈牙利树。那么这个是什么?匈牙利树他是这样的性质,从根节点到叶节点的路径均是交替路,且匈牙利树的叶节点都是匹配点

    例题:HDU-2063 过山车

    经典板子题。

    #include <bits/stdc++.h>
    #define MP make_pair
    #define PB push_back
    #define fi first
    #define se second
    #define ZERO(x) memset((x), 0, sizeof(x))
    #define ALL(x) (x).begin(),(x).end()
    #define rep(i, a, b) for (repType i = (a); i <= (b); ++i)
    #define per(i, a, b) for (repType i = (a); i >= (b); --i)
    #define QUICKIO                  
        ios::sync_with_stdio(false); 
        cin.tie(0);                  
        cout.tie(0);
    using namespace std;
    typedef long long ll;
    typedef int repType;
    const int MAXN=505;
    int k,m,n;
    bool mat[MAXN][MAXN],used[MAXN];
    int linker[MAXN];
    
    int dfs(int boy)
    {
    	rep(g,1,n) // girl
    	{
    		if(mat[boy][g] && !used[g])
    		{
    			used[g]=true;
    			if(linker[g]==-1 || dfs(linker[g]))
    			{
    				linker[g]=boy;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    
    int hungary()
    {
    	int ans=0;
    	memset(linker,-1,sizeof(linker));
    	rep(b,1,m) // boy
    	{
    		ZERO(used);
    		if(dfs(b)) ans++;
    	}
    	return ans;
    }
    
    int main()
    { 
    	while(cin>>k)
    	{
    		if(!k) break;
    		cin>>m>>n;
    		ZERO(mat);
    		rep(i,1,k)
    		{
    			int a,b; cin>>a>>b;
    			mat[a][b]=true;
    		}
    		cout<<hungary()<<endl;
    	}
    	return 0;
    }
    

    其他的题型

    简单改一下:HYSBZ - 1191 超级英雄Hero

    最小路径覆盖

    二分图最大独立集

    参考网址与资源

    https://www.renfei.org/blog/bipartite-matching.html
    https://www.cnblogs.com/YiXiaoZhou/p/5875040.html
    https://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html

    如非注明,原创内容遵循GFDLv1.3发布;其中的代码遵循GPLv3发布。
  • 相关阅读:
    平台调用中的数据封送处理
    JavaScript 中的事件流
    Jquery插件 表格固定表头
    ASP.NET MVC Action Filter与内置的Filter实现
    getCurrentScript的改进
    analyze spring framework source
    Windows Azure: Service Bus Brokered Messaging DeadLetterQueue 使用详解
    C#截图
    权限系统
    音乐播放器
  • 原文地址:https://www.cnblogs.com/samhx/p/Bipartite-Graph-Matching.html
Copyright © 2011-2022 走看看