• 二分图匹配学习笔记


    二分图的定义:##

    二分图的顶点可以被分为两个点集,且相同点集内的顶点之间没有连边

    二分图的判定:##

    dfs整个图并对其进行染色,且只能染两种颜色,若与其相邻的顶点与其同色则不是二分图,否则继续dfs。

    代码:###

    //-------------------二分图判定代码:---------------------------//
    int dfs(int u){    //染色为1或2 
    	int flag=0;		
    	for(register int v=1;v<=n;v++)
    		if(u!=v&&g[u][v]) {  //如果u和v之间有连边
    			if(color[v]==0) { //如果v没有染色 
    				color[v]=3-color[u];  //给v染色 
    				flag=dfs(v);  //继续dfs 
    				if(!flag)return 0;  //如果flag==0说明dfs的结果是非二分图 
    			}
    			else if(color[u]==color[v]) return 0;  //如果染了色且u和v同色则非二分图 
    		}
    	return 1;
    }
    

    二分图的匹配:##

    给定一个二分图G,M为G边集的一个子集且满足当中任意的两条边都依附于同一个顶点,则称M是G的一个匹配
    在所有匹配中边数最多的称最大匹配
    (简单来说就是在二分图里选一些边,让这些边没有公共顶点,从这个角度讲看起来有点像映射)

    求二分图最大匹配:##

    • 匈牙利算法
    • 最大流

    最大流就不介绍了因为笔者不会
    这里主要介绍匈牙利算法


    匈牙利算法:##

    匈牙利算法的核心是找增广路
    增广路(或增广轨、交错路):若P是图G中一条连通两个未匹配顶点的路径,且已匹配边和未匹配边在这条路径上交错出现,则称P是相对于当前匹配M的一条增广路

    由增广路的定义,可以推出三个结论

    • 结论1:P的路径长度(P内的边数)必定为奇数
      证明:由于起点和终点都是未匹配点,所以第一条边和最后一条边一定都是未匹配边,而由定义可以知道匹配边和未匹配边在增广路上交错出现,所以P的路径长度一定为奇数,且未匹配边比匹配边数量多1
    • 结论2:P经过取反操作可以得到一个更大的匹配M'
      *取反:未匹配边->匹配边,匹配边->未匹配边
      从结论1的证明知道未匹配边比匹配边数量证明多1,所以取反得到的匹配边数将比原匹配边数多1
    • 结论3:M为G的最大匹配当且仅当不存在相对于M的增广路
      反证,如果有的话还可以扩大匹配边数,M就不是G的最大匹配了

    所以根据以上三条结论设计出匈牙利算法的流程:

    匈牙利算法流程:###

    • STEP 1 对于一个没有匹配的顶点u,遍历其每条边,如果这条边上的另一个顶点v还未匹配则说明找到了一个匹配,转STEP 4
    • STEP 2 若v已经被匹配,则寻找v的匹配点w,对于w,再遍历其边,找一个未匹配顶点(即反复STEP 1,2 以寻找增广路)
    • STEP 3 若在STEP1,2的反复过程中找到一条增广路,修改各自的匹配点并转STEP 4,若未找到增广路则退出
    • STEP 4 匹配数++

    形象来讲匈牙利算法就是一个找妹子和绿与被绿的过程有机会就上,没机会把原配挤掉也要上,只要你能给原配牵个新妹子

    代码:###

    我搜了一下,网上流传的大多是邻接矩阵下的匈牙利算法,但邻接矩阵效率较低(尤其在稀疏图上),且空间复杂度较高,所以这里提供一种邻接表的匈牙利算法,其本质是一样的。

    //-------------------------匈牙利算法:--------------------------//
    int find(int u){
    	for(register int i=first[u];i;i=nxt[i]) {
    		int v=to[i];
    		if(vis[v]) continue;
    		else {
    			vis[v]=true;
    			if(match[v]==-1||find(match[v])) {        //如果v未被匹配或从v开始继续寻找找到了一条增广路 
    				match[v]=u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    int hungary(){
    	for(register int i=1;i<=n;i++){
    		for(register int j=1;j<=n;j++)vis[j]=false;
    		ans+=find(i);
    	}
    }
    

    例题及后续拓展走这里:传送门

  • 相关阅读:
    body中的font属性和color属性
    div 的position属性
    border属性
    html header标签
    关于web.config中<customErrors>节点说明
    STL 小白学习(1) 初步认识
    sin n次方 x 的降幂公式
    查找结构体数组中的人名是否匹配
    将结构体数组中内容以文件形式的导出
    python 小白学习(1)
  • 原文地址:https://www.cnblogs.com/kma093/p/10310272.html
Copyright © 2020-2023  润新知