二分图:图中有两个点集,集合内的点互不连边,两个集合的点互相连边
二分图最大匹配:在二分图找到最多的匹配数 匈牙利算法: 时间复杂度: 邻接矩阵:O(n^3) 邻接表:O(n*m)
int e[N][N]; //邻接矩阵 //可以用邻接表稍微优化一下
int vis[N],link[N]; //link[i]表示编号为i的人的对象是谁(编号为i的女生喜欢的男生是谁) //vis[i]表示i是否在增广路上出现过
int dfs(int x){ //男生x
for(int i=1;i<=n;++i){ //女生i
if(!e[i][x]||vis[i]) continue; //!e[i][x]表示i和x无边即i和x并不相互喜欢
//vis[i]表示i已经出现在这条增广路(交错路)上h,此时连此边则必不能增广;或 曾经搜过此点结果并不可行(可行当时就return了);所以continue
vis[i]=1; //vis标记的是女生即右点集
if(link[i]==0||dfs(link[i])){ // link[x]==0表示x没有对象 //dfs找i的对象能否找到新的对象 //dfs女生i的心仪男生能否找到新的女生
link[i]=x; //女生i的现男友是x //i和x配对成功
return 1; //配对成功返回1
}
}
return 0; //配对失败返回0
}
int main(){
for(int i=1;i<=n;++i){
memset(vis,0,sizeof vis);
dfs(i); //搜索第i个男生能否配对 //即i是否增广成功
}
}
匈牙利搜的是男生,原配增广的也是男生,即只有原配男生增广成功,女生才能和新男生配对
konig定理:二分图的最大匹配数==最小点覆盖数 最小点覆盖:每个点覆盖以它为端点的所有边,选择最少的点来覆盖所有的边
图中的边就是关系 完美匹配:所有人都匹配上 最优匹配:是完美匹配(实际上n<=m,n是左点集的个数,m是右点集的个数),且匹配得分即满意度最高 匈牙利+降低标准(修改点标) O(nnm)【此复杂度为使用邻接表的复杂度】
int const INF=0x7f7f7f7f;
int dfs(int x){
visx[x]=1;
for(int i=1;i<=m;++i){
if(!visy[i]&&l[x]+r[i]==e[x][i]){
visy[i]=1;
if( !link[i] || dfs(link[i]) ){
link[i]=x;
return 1;
}
}
}
return 0;
}
int solve(){
memset(l,-0x7f,sizeof l); //左点集
memset(r,0,sizeof r); //右点集
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j) l[i]=max(l[i],e[i][j]); //令左点标等于最大边权
}
for(int i=1;i<=n;++i){ //为每个点找匹配
while(1){ //若没找到匹配,则修改点标即加边 //最坏情况下,每个点都要修改,O(n)的复杂度(此循环)
memset(visx,0,sizeof visx);
memset(visy,0,sizeof visy);
if(dfs(i)) break;
int d=INF;
//以下为降低标准,即加边(修改点标)
for(int lz=1;lz<=n;++lz){ //n是左点集
if(visx[lz]) {
for(int rz=1;rz<=m;++rz){ //m是右点集,从代码和算法来看 n<=m
if(!visy[rz]) d=min(l[lz]+r[rz]-e[lz][rz]);
}
}
}
if(d==INF) return -1; //若没有加边成功,则说明没有最优匹配
for(int j=1;j<=n;++j) if(visx[j]) l[j]-=d;
for(int k=1;k<=m;++k) if(visy[k]) r[j]+=d;
}
}
}
所有二分图的题都可以用网络流解