增广路定理:我们用未盖点来表示不与任何匹配边邻接的点,其他点为匹配点,即恰好和一条匹配边邻接的点。从未盖点出发,依次经过非匹配边,匹配边,非匹配边,匹配边……所得到的路称为交替路。如果交替路的终点是一个未盖点,则称这条交替路为一条增广路。增广路中,非匹配边比匹配边多一条。
增广路的作用是改进匹配,假设我们已经找到一个匹配,如何判断他是否是最大匹配? 看增广路,如果有一条增广路,那么把此路上的匹配边和非匹配边互换,得到的匹配比刚才的多一条。 反之,若找不到增广路,则当前匹配就是最大匹配
一个匹配是最大匹配的充要条件是不存在增广路,这个充要条件适用于任意图。
增广路算法:根据增广路定理,最大匹配可以通过反复勋章增广路来求解,如何找到增广路呢?根据定义,首先需要选一个未盖点u作为起点,设这个u是X结点。接下来需要选一个从u出发的非匹配边(u,v) 到达Y结点v。如果v是未盖点,说明我们成功找到了一条增广路,如果v是匹配点,那我们下一步得走匹配边,因为一个匹配点恰好与一个匹配边邻接。设匹配点v邻接的匹配边的另一端为left[v],那么可以理解从u直接走到了left[v],而这个left[v]也是一个X结点。如果始终没有找到未盖点,最后会扩展出一颗匈牙利树。
这样,我们得到了一个算法,即每次选一个未盖点u进行DFS,注意,如果找不到以u开头的增广路,则换一个未盖点进行DFS,且以后再也不从u出发找增广路。换句话说,如果以后存在一个从u出发的增广路,那么现在就找得到。
#include
#include
#include
using namespace std;
const int maxn=600+5;
bool line[maxn][maxn];
//line[x][y]=true表示x号女生喜欢y号男生(边)
int boy[maxn];
//存储y号男生匹配边另一端的匹配点女生,如果是未盖点则为0
bool use[maxn];
//存储y号男生是否在这条交替路上被使用过
int k,n,m;
bool dfs(int x){
for(int j=1;j<=m;j++){
if(line[x][j]&&!use[j]){
use[j]=true;
if(!boy[j]||dfs(boy[j])){
//满足上面的判断语句说明找到了增广路
//即终点是未盖点的交替路
boy[j]=x;
return true;
}
}
//该条交替路不是增广路则需要找下一条交替路
}
return false;
//无法找到增广路则未盖点x的dfs失败,不存在以x为起点的增广路
}
int main(){
// freopen("datain.txt","r",stdin);
while(cin>>k){
if(!k) break;
cin>>n>>m;//k为边数,n为女生数,m为男生数
int x,y;int ans=0;
memset(line,false,sizeof(line));
memset(boy,0,sizeof(boy));
while(k--){
cin>>x>>y;
line[x][y]=true;
}
for(int i=1;i<=n;i++){
memset(use,false,sizeof(use));
if(dfs(i))ans++;//为一个女生dfs成功一次,则ans++
}
cout<