2019 UESTC ACM Training for Graph[J] (二分图匹配+Hopcroft-Carp)

知识共享许可协议 Creative Commons

题意:给定一个二分图,选择尽量少的节 点使每条边都至少有一个点被选择,求选 择节点数

题目中就很明确的把点分成了左右两边,几乎就是暗示我们是一个二分图匹配。下面是对二分图匹配正确性简单的说明:
假设对于当前的图已经找到了其最大二分图匹配,那么对于剩下的边集合有这几种情况:
1.两个端点都在最大匹配的点集之外,那么这种情况其实也会使得最大匹配数+1,即之前求出来的并不是最大匹配,与题设矛盾。
2.至少有一个端点在最大匹配的点集之中,那么这条边就符合题目中的意思。
因此,只需求出最大匹配数即为所求答案。不过匈牙利算法会TLE,因此使用Hopcroft-Carp算法解决。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
struct Edge{
	int from,to;
	Edge(int from,int to):from(from),to(to){}
};
vector<int>G[maxn];
vector<Edge>edges;
void add_edges(int from,int to){
	edges.push_back(Edge(from,to));
	G[from].push_back(edges.size()-1);
}
int a,b,n,m,vis[maxn],cx[maxn],cy[maxn];
int dx[maxn],dy[maxn],dis;
bool searchp(){
	queue<int>q;
	dis=inf;
	memset(dx,-1,sizeof(dx));
	memset(dy,-1,sizeof(dy));
	for(int i=1;i<=a;i++)
		if(!cx[i]){
			q.push(i);
			dx[i]=0;
		}
	while(q.size()){
		int u=q.front();q.pop();
		if(dx[u]>inf)break;
		for(int i=0;i<G[u].size();i++){
			Edge& e=edges[G[u][i]];
			if(dy[e.to]==-1){
				dy[e.to]=dx[u]+1;
				if(!cy[e.to])dis=dy[e.to];
				else {
					dx[cy[e.to]]=dy[e.to]+1;
					q.push(cy[e.to]);
				}
			}
		}
	}
	return dis!=inf;
}
int find(int u){
	for(int i=0;i<G[u].size();i++){
		Edge& e=edges[G[u][i]];
		if(vis[e.to]==0&&dy[e.to]==dx[u]+1){
			vis[e.to]=1;
			if(cy[e.to]!=0&&dy[e.to]==dis)continue;
			if(!cy[e.to]||find(cy[e.to])){
				cx[u]=e.to;
				cy[e.to]=u;
				return 1;
			}
		}
	}
	return 0;
}
int maxmatch(){
	int sum=0;
	while(searchp()){
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=a;i++)
			if(!cx[i]&&find(i))sum++;
	}
	return sum;
}
int main(){
	_read(a);_read(b);_read(m);
	n=a+b;
	for(int i=1;i<=m;i++){
		int x,y;
		_read(x);_read(y);
		add_edges(x,y);
	}
	cout<<maxmatch()<<endl;
}

猜你喜欢

转载自blog.csdn.net/getsum/article/details/91845759