二分图

每条边的两个结点都分别在X点集,和Y点集中。

匹配

集合M中存的每一条边的端点都不会重复
最大匹配:使集合大小最大(可能有多个最大匹配)

增广路径

在匹配M中的一条交错路径,它的两个端点都不与M中的边关联
ps.交错指一条不属于M的边+属于M的边+不属于M的边...
出现一条新的增广路径则匹配成功的边数+1,因为交错的边反转(属于<->不属于)。

匈牙利算法(hungry算法)

用途:求二分图的最大匹配
流程:
用link表示Y集合中的点当前配对的X集合中的点
(假设点属于X,Y集合)
用每个X集合中的点去配Y集合中的点,若不改变前面匹配出的最大边数出现新的增广路径,最大边数+1
下面这份代码的核心:

能否找到两个端点是都没被匹配过的,能,就在回溯的时候就将增光路的匹配情况取反

代码:

    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int nxt[N],link[N],to[N],head[N],num;
    bool road[N];
    void add_edge(int u,int v) {
    	num++; nxt[num]=head[u]; to[num]=v; head[u]=num;
    }
    bool Find(int u) {
    	for(int i=head[u];i;i=nxt[i]) {
    		int v=to[i];
    		if(!road[v]) {
    			road[v]=true;
    			if(!link[v]||Find(link[v])) {		//若v原来已经属于link[v]了那,试图让link[v]去连别的边,如果成功(Find(link[v])==true)那就更新link[v].
    				link[v]=u;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    int main() {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) {
    		int c,v;
    		scanf("%d",&c);
    		for(int j=1;j<=c;j++) {
    			scanf("%d",&v);
    			add_edge(i,v);
    		}
    	}
    	int tot=0;
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) road[j]=false;
    		if(Find(i)) tot++;
    	}
    	printf("%d",tot);
    	return 0;
    }