二分图匹配:匈牙利算法

二分图

  • 点能分成两个独立的点集

二分图匹配:匈牙利算法_第1张图片

  • 匹配点:匹配边上的两点
  • 最大匹配:选出最大的边数,使得这些边的顶点不重复
  • 完美匹配:所有顶点都是匹配点。完美匹配一定时最大匹配,最大匹配不一定是完美匹配
  • 最小覆盖:分为最小顶点覆盖与最小路径覆盖
  • 最小顶点覆盖=最大匹配。选出最少的点集,覆盖所有的边
  • 最小路径覆盖=n-最大匹配。(n为所有顶点)用最少的不相交路径覆盖所有点
  • 最大独立集:n-最大匹配。(n为所有顶点)找出一个点集,使得点集中任意两点在图中无边。最大独立集与最小点覆盖互补。
  • 最大团=补图的最大独立集。选出一些顶点,使得这些点两两有边

匈牙利算法

匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

时间复杂度邻接矩阵:最坏为O(n^3)邻接表:O(mn)
空间复杂度 邻接矩阵:O(n^2) 邻接表:O(m+n)

例子:

一: 先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,got it,连上一条蓝线

:接着给2号男生找妹子,发现第一个和他相连的2号女生名花无主,got it

:接下来是3号男生,很遗憾1号女生已经有主了,怎么办呢?

我们试着给之前1号女生匹配的男生(也就是1号男生)另外分配一个妹子。

(黄色表示这条边被临时拆掉)

重新找个妹子(注意这个步骤和上面是一样的,这是一个递归的过程)

此时发现2号男生还能找到3号女生,那么之前的问题迎刃而解了,回溯回去。

2号男生可以找3号妹子~~~                  1号男生可以找2号妹子了~~~                3号男生可以找1号妹子

所以第三步最后的结果就是:

: 接下来是4号男生,很遗憾,按照第三步的节奏我们没法给4号男生腾出来一个妹子,我们实在是无能为力了……香吉士同学走好。

这就是匈牙利算法的流程,其中找妹子是个递归的过程,最最关键的字就是“腾”字===============================================================================

邻接表:o(mn)

羊吃草

theme:n只羊在一条长度为400的线段上,每只羊只会在它喜欢的区间[a,b]吃草,只会位于整数点处,且每时刻每个点只会有一只羊,先q次询问,每次询问区间[l,r]中最多有多少只羊吃草。1<=n,q<=400

solution:二分匹配。将羊与它喜欢的区间相连,则进行q次匹配,在匈牙利算法dfs为给定节点u配对是判断一下v是否位于[l,r]范围即可。

//匹配指定右区间
#include
using namespace std;
#define re(i,n) for(int i=0;iG[N];
int n,m,q;
int l,r;

void createG()
{
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=a[i];j<=b[i];++j)
            G[i].push_back(j);
    }
}

bool dfs(int u)
{
	for (int i=0;i=l&&v<=r && used[v]==false)
		{
			used[v]=1;
			if (linker[v]==0 || dfs(linker[v])) {////名花无主或者能腾出个位置来,这里使用递归
				linker[v]=u;
				return true;
			}
		}
	}
	return false;
}

//匈牙利算法求二分图最大匹配
int hungary()
{
    int ans=0;
    fill(linker,linker+m+5,0);
    for(int u=1; u<=n; ++u)//给每个左节点找右节点
    {
        fill(used,used+m+5,0);
        if(dfs(u))//如果能成功给节点u找到一个匹配的右节点,且不影响之前选过的节点,则结果+1
            ans++;
    }
    return ans;
}

int main()
{
    cin>>n>>q;
    m=401;
    createG();
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&l,&r);
        int ans=hungary();
        printf("%d\n",ans);
    }
}

邻接矩阵:o(n^3)

poj1274:The Perfect Stall

theme:n只牛m个坑,每只牛一个坑,每个坑只能占一只牛,给出每只牛能取哪几个坑,问最多能安排好多少只牛占一个坑。0 <= N,M<= 200

solution:裸的二分图匹配

//theme:n只牛m个坑,每只牛一个坑,每个坑只能占一只牛,给出每只牛能取哪几个坑,问最多能安排好多少只牛占一个坑。0 <= N,M<= 200
#include
#include
#include
using namespace std;
#define re(i,n) for(int i=0;i

最大团

poj3692:Kindergarten

theme:n个男生,m个女生,其中女生之间、男生之间相互认识,有些女生与有些男生相互认识,问最能能从中选出多少人来是的任意两人相互认识?1 ≤ GB ≤ 200

solution:由题意可知是求最大团,最大团为选最大的点集使得任意两点之间都有边,而最大团=补图的最大独立集,最大独立集=总的点数-最大匹配

//theme:n个男生,m个女生,其中女生之间、男生之间相互认识,有些女生与有些男生相互认识,问最能能从中选出多少人来是的任意两人相互认识?1 ≤ G, B ≤ 200
//最大团
#include
#include
#include
using namespace std;
#define re(i,n) for(int i=0;i

二分多重匹配

hdu3605:Escape

theme:n个人,m间房,给出每个人能去哪几间房和房间最大容量,问这n个人能否都入住?1 <= n <= 100000, 1 <= m <= 10.

solution:二分多重匹配.

可以用网络流,加上一个源点与汇点。但节点数太大,可以考虑状态压缩,把n个人按能去的房间数组合,最多有2^10种

//theme:n个人,m间房,给出每个人能去哪几间房和房间最大容量,问这n个人能否都入住?1 <= n <= 100000, 1 <= m <= 10.
//邻接矩阵的匈牙利多重匹配(右节点有容量)
#include
#include
using namespace std;
#define re(i,n) for(int i=0;i

 

 

 

 


匈牙利算法参考:https://blog.csdn.net/dark_scope/article/details/8880547

你可能感兴趣的:(二分匹配)