P3386 【模板】二分图最大匹配 题解

CSDN同步

原题链接

简要题意:

给定两个点集与一些边(保证每条边的顶点属于不同点集),求二分图最大匹配。

解释下:二分图最大匹配 就是 最大的边集使得每两条边都不相交,不共点 的边的个数

那么你会说,我用并查集维护一下就行了?远远不是这样。

本题作为 匈牙利算法 的模板题讲解。

以下面这个关系为例:

假设有 wxqwsqzmxNBLbfwgyxkkk 六个人。

其中,wxqzmxgyxbfw是第一点集,其余是第二点集。

并且他们说:

  • wxq: 我觉得 wsqNBLkkk 都可以成为我的 \(\text{CP}\)

  • zmx: 我觉得只有 wsq 可以成为我的 \(\text{CP}\).

  • gyx: 我觉得 wsqNBL 都可以食用。

  • bfw: 我觉得她们三个都不错啊。

好,下面我们要 尽量满足他们的需求

第一个人 wxq,好,他的第一个备选人是 wsq,那么就先让他和 wsq 成为 \(\text{CP}\).

第二个人 zmx 来了,并对 wxq 说:

你拿走了 wsq 那我怎么办???

wxq 想:行啊,反正我还有 NBLkkk.

于是,wxq被绿了 zmx 成功地 ~绿了wxq 得到了 wsq,然后 wxqNBL 成为 \(\text{CP}\).

然后,第三个人 gyx 来了,并对 zmx 说:

你拿走了 wsq 那我怎么办?

zmx 想了想说:

我只有她一个备选人啊,我要生存!单身狗还是你做吧

于是 gyx 绿人计划失败

但是 gyx 看自己的备选人还有 NBL,所以对 wxq 说:

喂喂喂,快点还我 NBL.

wxq 想:没关系,反正我还有 kkk 对吧。

所以 wxq又被绿了 gyx 绿了wxqNBL 成为了 \(\text{CP}\),然后 wxq被轮番绿kkk 成为了 \(\text{CP}\).

最后一个人 bfw 来了,他先找到 zmx 说:

wsq 给我行不?

zmx 说:我只有她一个人啦!对方拒绝了该请求。

bfw 又辗转来到 wxq 说:

kkk 给我行不?

wxq 说:你们都想绿我是吧,没门 我也只有她一个人了。

bfw 最终对 gyx 说:

救救我,我马上要单身了。

gyx 一口回绝道:我刚刚绿了 wxq,你就像反绿我是吧 我也只有 NBL 一人了,你再找找别人吧。

最后,bfw 成为了单身狗 被抛弃了,他没有 \(\text{CP}\),所以上述的 二分图最大匹配\(3\).(满足了 \(3\) 个人的需求)

所以可见,匈牙利算法是一种基于 绿与被绿 协商与匹配的过程。

简要步骤如下:

  1. 如果后来的人 想绿别人 与前面的人发生冲突,前面的人会进行抉择进行第 \(2\) 步;否则后来的人就有了 \(\text{CP}\). 有了,下一个!

  2. 如果前面的人发现 自己被绿了,还有退路或者能绿别人 自己没有 \(\text{CP}\) 可处,那么就拒绝;然后后来的人回到第 \(1\) 步,进行下一步 准备绿人

  3. 如果新来的那人被所有人拒绝了 绿人失败,则它就单着吧。 单身狗它不香吗

众所周知,只有最后绿人成功的人才能获得最终胜利!

有机会上,没机会创造机会也得上!

这就是 匈牙利算法 的大致内容。

你会发现,可能会出现 很多人迭代地绿对方 很多 协商与匹配的过程。 就比方说一个完全图(只有被绿了多次的人才能找到真正的CP,而且先下手为强),所以我们要用 \(\text{dfs}\) 进行递归搜索。

  • 细节:

有的时候自己会 间接地绿自己 与自己协商。比方说:

1 1
1 2
2 1
3 1
3 2

\(1\)\(2\) 绿 协商后,\(1 \rightarrow 2\)\(2 \rightarrow 1\),然后 \(3\) 来了。他 想绿1 先与 \(1\) 协商,然后 \(1\)\(2\) 协商(循环被绿),然后 \(2\) 又和 \(1\) 协商,\(1\) 又与 \(2\) 协商。。最后3成单身狗,他俩绿疯了 然后就没了。 3:喂你们没人选我么

所以,要用 \(vis_i\) 表示 当前 想绿人的 与别人协商的是几号,防止循环 绿 协商。

时间复杂度:\(O(\max(n,m) \times e)\)

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=2e3+1;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,m,T,vis[N],mat[N];
//mat[i] 表示 i 的 CP 编号
vector<int>G[N]; //图

inline bool dfs(int dep,int bs) { //表示当前让 CP 是否成功
	if(vis[dep]==bs) return 0;
	vis[dep]=bs;
	for(int i=0;i<G[dep].size();i++) {
		int x=G[dep][i];
		if(!mat[x] || dfs(mat[x],bs)) {
		//没有匹配 或者 可以让 CP 就换 CP
			mat[x]=dep; //记录匹配
			return 1;
		}
	} return 0;
}

int main(){
	n=read(),m=read(),T=read();
	while(T--) {
		int x=read(),y=read();
		G[x].push_back(y);
	} int ans=0;
	for(int i=1;i<=n;i++)
		if(dfs(i,i)) ++ans;
	printf("%d\n",ans);	
	return 0;
}

posted @ 2020-04-02 17:20  bifanwen  阅读(251)  评论(0编辑  收藏  举报