• 二分图匹配


    相关定义

    • 匹配

    在图论中,一个匹配是一个边集,其中任意两条边都没有公共顶点。

    • 最大匹配

    一个图的所有匹配中元素最多的匹配就是最大匹配

    • 完美匹配

    若一个图中的某个匹配中任意一个顶点都是匹配点(即是一条匹配边的端点),则称这个匹配是这个图的完美匹配。

    • 二分图

    若一个图的顶点能被分成 (X)(Y) 两个集合且任意两个位于同一集合内部的点之间没有边相连,那称这个图为可二分的。

    现阶段(NOIP)只关注二分图的匹配问题。一般图的匹配是NOI+。

    匈牙利算法

    求二分图的最大匹配,现在常用的就是费用流和匈牙利

    再次给出一些定义:

    • 交替路

    从一个未匹配点出发,依次经过“非匹配边 — 匹配边 — 非匹配边 — (cdots)”形成的路径叫做交替路。

    • 增广路

    从一个未匹配点出发,依次经过“非匹配边 — 匹配边 — 非匹配边 — (cdots) — 非匹配边”形成的路径叫做增广路。(其实就是最后经过边是未匹配边的交替路)

    由定义易得到,每次找到一条增广路,将边取反(匹配边变为非匹配边,非匹配边变为匹配边,可行性证明略手模一下就能理解),就可以使匹配边数量 (+1)

    这就是匈牙利算法的基本原理

    假设我们已经找到了一个匹配,想要求一个更大的匹配。

    我们可以试着考虑从一个未匹配点开始DFS,若找到一个增广路,就代表还有更大匹配。

    若增广失败,则此时的匹配为最大匹配。

    code:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=510,M=1e5+10;
    
    int n1,n2,m;
    int head[N],nxt[M],ver[M],tot=0;
    void add(int x,int y)
    {
    	ver[++tot]=y;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    int match[N];	//每一个左部点对应的右部点 
    bool vis[N];	//标记 
    
    bool find(int x)
    {
    	for(int i=head[x];i;i=nxt[i])
    	{
    		int y=ver[i];
    		if(!vis[y]) 
    		{
    			vis[y]=1;
    			if(match[y]==0||find(match[y]))		//若未匹配or重匹配成功 
    			{
    				match[y]=x;		//更新匹配 
    				return 1; 
    			}
    		}
    	}
    	return 0;
    }
    
    int main()
    {
    	scanf("%d%d%d",&n1,&n2,&m);
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);	//虽然是无向图,但是我们只需要建从左到右的点 
    	}
    	
    	int res=0;
    	for(int i=1;i<=n1;i++)
    	{
    		memset(vis,0,sizeof vis);	//清空标记数组
    		if(find(i)) res++;//如果成功找到增广,答案加一 
    	}
    	printf("%d",res);
    	return 0;
    }
    
  • 相关阅读:
    运算符
    变量
    JSP EL表达式使用
    MySQL JDBC 连接数据库基本操作
    一个带标号的CSS文章列表写法
    CSS图片列表
    YUI3 CSS
    Ubuntu 13.10 64位 无法 安装 ia32-libs 解决办法
    [转]编译Android源代码常见错误解决办法
    js 复制对象
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/13909520.html
Copyright © 2020-2023  润新知