二分图匹配
匈牙利算法
https://blog.csdn.net/c20180630/article/details/70175814
增广路径
增广路径的定义:设M为二分图G已匹配边的集合,若P是图G中一条连通两个未匹配顶点的路径(P的起点在X部,终点在Y部,反之亦可),并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。
增广路径是一条“交错轨”。也就是说, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且起点和终点还没有被选择过,这样交错进行,显然P有奇数条边
由增广路的定义可以推出下述三个结论:
- P的路径长度必定为奇数,第一条边和最后一条边都不属于M,因为两个端点分属两个集合,且未匹配。
- P经过取反操作可以得到一个更大的匹配M’。
- M为G的最大匹配当且仅当不存在相对于M的增广路径。
匈牙利算法
算法轮廓:
- 置M为空
- 找出一条增广路径P,通过取反操作获得更大的匹配M’代替M
- 重复2操作直到找不出增广路径为止
模板题hdu2063
#include<bits/stdc++.h> using namespace std; const int N=550; int k,m,n,cnt; bool vis[N]; int ma[N],last[N]; struct orz{ int v,nex;}e[N*4]; void add(int x,int y) { cnt++; e[cnt].v=y; e[cnt].nex=last[x]; last[x]=cnt; } bool found(int x) { for (int i=last[x];i;i=e[i].nex) { if (!vis[e[i].v]) { int v=e[i].v; vis[v]=1; if (!ma[v]||found(ma[v])) { ma[v]=x; return 1; } } } return 0; } int main() { while (scanf("%d",&k)&&k) { scanf("%d%d",&m,&n); memset(ma,0,sizeof(ma)); memset(e,0,sizeof(e)); memset(last,0,sizeof(last)); cnt=0; int x,y; for (int i=1;i<=k;i++) { scanf("%d%d",&x,&y); add(x,y); } int ans=0; for (int i=1;i<=m;i++) { memset(vis,0,sizeof(vis)); if (found(i)) ans++; } printf("%d\n",ans); } }
KM算法
https://blog.csdn.net/sixdaycoder/article/details/47720471
Kuhn-Munkras算法(即KM算法)流程:
- 初始化可行顶标的值 (设定lx,ly的初始值)
- 用匈牙利算法寻找相等子图的完备匹配
- 若未找到增广路则修改可行顶标的值
- 重复(2)(3)直到找到相等子图的完备匹配为止
模板题hdu2255
#include<bits/stdc++.h> using namespace std; const int N=550; const int INF=0x3f3f3f3f; int k,m,n,cnt; bool visx[N],visy[N]; int ma[N],slack[N],lx[N],ly[N],G[N][N]; bool found(int x) { visx[x]=1; for (int y=1;y<=n;y++) { if (!visy[y]) { int now=lx[x]+ly[y]-G[x][y]; if (now==0) { visy[y]=1; if (ma[y]==-1||found(ma[y])) { ma[y]=x; return 1; } } else slack[y]=min(slack[y],now); } } return 0; } void KM() { for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) slack[j]=INF; while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if (found(i)) break; int minm=INF; for (int j=1;j<=n;j++) if (!visy[j]) minm=min(minm,slack[j]); for (int j=1;j<=n;j++) if (visx[j]) lx[j]-=minm; for (int j=1;j<=n;j++) if (visy[j]) ly[j]+=minm; else slack[j]-=minm; } } } void solve() { memset(ma,-1,sizeof(ma)); memset(ly,0,sizeof(ly)); for (int i=1;i<=n;i++) { lx[i]=-INF; for (int j=1;j<=n;j++) lx[i]=max(lx[i],G[i][j]); } KM(); } int main() { while (scanf("%d",&n)!=EOF) { for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&G[i][j]); solve(); int ans=0; for (int i=1;i<=n;i++) if (ma[i]!=-1) ans+=G[ma[i]][i]; printf("%d\n",ans); } }