题意:给定一个二分图,选择尽量少的节 点使每条边都至少有一个点被选择,求选 择节点数
题目中就很明确的把点分成了左右两边,几乎就是暗示我们是一个二分图匹配。下面是对二分图匹配正确性简单的说明:
假设对于当前的图已经找到了其最大二分图匹配,那么对于剩下的边集合有这几种情况:
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;
}