题目背景
二分图
题目描述
给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数
输入输出格式
输入格式:第一行,n,m,e
第二至e+1行,每行两个正整数u,v,表示u,v有一条连边
输出格式:共一行,二分图最大匹配
输入输出样例
输入样例#1:
1 1 1 1 1
输出样例#1:
1
说明
n,m<=1000,1<=u<=n,1<=v<=m
因为数据有坑,可能会遇到v>m的情况。请把v>m的数据自觉过滤掉。
算法:二分图匹配
增广路定义:从一个未被选中的点出发,依次经过非匹配边,匹配边,非匹配边,匹配边……,
如果终点是一个未被选择的点,则为增广路。
这里讲一下我的理解:
在一个增广路中,非匹配边总比匹配边多一条。那么,对于一种已匹配好的情况,如果
能找出一条增广路,那么原情况就不是最优的,替换它。如此下去,就能找出二分图的最大匹配。
所以,对于1……n的点,每次都DFS一下是否有增广路,如果有,答案+1。
个人感觉这种算法是比较妙的,虽然没有网络流跑的快,但有一定的思维深度(还是画画图更好理解)
#include<set> #include<map> #include<queue> #include<cmath> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define INF 2147483647 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,e,f[2000][2000],link[2000],vis[2000];
//f表示左右是否有边
//link[i]表示当前的最大匹配中,右边的i对应的点
//vis[i]表示当前这个点是否匹配过了,所以要每次都更新 int dfs(int x){ for(int i=1;i<=m;++i) if(!vis[i]&&f[x][i]){ //注意顺序,常数优化,不然会TLE vis[i]=1; if(!link[i]||dfs(link[i])){ link[i]=x; return 1; } } return 0; } int main(){ n=read();m=read();e=read(); while(e--){ int x,y;x=read();y=read(); f[x][y]=1; } int ans=0; for(int i=1;i<=n;++i){ memset(vis,0,sizeof(vis)); if(dfs(i)) ans++; } printf("%d",ans); return 0; }
https://www.luogu.org/problem/show?pid=3386