二分图最大匹配(匈牙利算法)

题目背景

二分图

题目描述

给定一个二分图,结点个数分别为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

原文地址:https://www.cnblogs.com/hanyu20021030/p/6530729.html