匈牙利。
匈牙利算法,能在 \(O(Lm+R)\) 的复杂度处理二分图最大匹配的问题,其中 \(L\) 是左部点个数,\(R\) 是右部点个数,\(m\) 是边数,马良极短,吊打网络流。
首先我们有很多关键点能想到这个,要么是涉及到匹配,或者是涉及到需要取出一些环的问题,这个是一个极好的处理方式。
算法流程就是一种调整法,考虑就是对于一个点,暴力去尝试匹配,如果不能匹配上它就失配了。暴力的过程则分为两种:
-
若该右部点没有匹配,直接匹配该右部点。
-
若该右部点有匹配,尝试让原来匹配的那个点找到新的匹配。
以下是简短的代码:
#include<bits/stdc++.h>
using namespace std;
const int M=1e3+5;
int mtch[M];
int visT[M];
int n,m,e,x,y;
vector<int> E[M];
int ans;
bool dfs(int now,int tg){
if(visT[now]==tg)return 0;
visT[now]=tg;
for(auto p:E[now])if(!mtch[p]||dfs(mtch[p],tg)){
mtch[p]=now;
return 1;
}
return 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>e;
for(int i=1;i<=e;i++)cin>>x>>y,E[x].push_back(y);
for(int i=1;i<=n;i++)ans+=dfs(i,i);
cout<<ans;
return 0;
}
其中 dfs
函数就是尝试匹配的最主要过程。递归的过程就是找新匹配的过程。为什么可以用 vis
数组?因为再次访问到只有两种情况,要么是再次尝试替换这个点,要么是替换成环,如果我们找到匹配就退出,那么这两种情况一定无解。
例题:CF387D George and Interesting Graph
George 喜欢图,因此它想让图更有趣。他认为有趣的图满足以下条件:
- 没有重边;
- 存在结点 \(v\)(称为中心),使得对于图中的任意结点 \(u\),都有边 \((u,v)\) 和 \((v,u)\),注意自环 \((v,v)\) 也应该存在;
- 除去中心外,每个点的入度和出度都恰好为 \(2\);
显然很少有图有趣,但 George 可以把图变得有趣:每次他可以增加一条边或者删除一条已经存在的边。
现在给出图 \(G\),George 想知道他最少做多少次操作可以使它变得有趣。
\(n\leq 500,m\leq 1000\)。
枚举中心,剩下就变成尝试连出一些简单环使得一个点在且仅在一个简单环里,这个直接上二分图匹配就能求出最多有多少条已有边在这个系统里。