KM算法一般用来寻找二分图的最优匹配。
步骤:
1.初始化可行标杆
2.对新加入的点用匈牙利算法进行判断
3.若无法加入新编,修改可行标杆
4.重复2.3操作直到找到相等子图的完全匹配。
各步骤简述:
1.根据二分图建立2个可行标杆;
lx为x的可行标杆,初始化lx[i]为与i点相连的最大边
ly为y的可行标杆,初始化为0.
可行性的判断条件应为lx[x]+ly[y] >= Map[x][y]。
2.对于新加入的点用匈牙利算法经行判断,确定改点能否加入旧子图中,形成新子图。
对于该加入的点有两种可能:
2.1.找到某个y点,该y点为被连接,则与该点相连,并符合条件
2.2找到某个y点,该y点已经被某x1点所连接,则对x1点经行深搜,若x1可以找到新的连接点,则修改x1的连接路径,并使x点与y点相连。
3.如果发现无法再向已有的相等子图中加入边,则证明不再有lx[x]+ly[y]==Map[x][y]成立,那么我们对x和y的可行标杆经行修改。
当找不到增广路径时,对于搜索过的路径上的XY点,设该路径上的X顶点集为S,Y顶点集为T,对所有在S中的点xi及不在T中的点yj,计算d=min{(L(xi)+L(yj)-weight(xiyj))},从S集中的X标杆中减去d,并将其加入到T集中的Y的标杆中,由于S集中的X标杆减少了,而不在T中的Y标杆不变,相当于这两个集合中的lx(x)+ly(y)变小了,也就是,lx[x]+ly[y]将变小,便有可能有lx[x]+ly[y]==Map[x][y]成立,即可能会加入新的边。
4.重复2.3过程,知道找到完备相等子图为止。
时间复杂度:优化后大概为O(n^3);
代码:
#include<cstdio> #include<stdio.h> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> #define INF 0x3f3f3f3f #define MAX 1005 #define mod 1000000007 using namespace std; int visx[MAX],visy[MAX],lx[MAX],ly[MAX],linker[MAX],slack[MAX],Map[MAX][MAX],n; bool DFS(int x) { visx[x]=1; for(int y=1;y<=n;y++) { if(visy[y]) continue; int tmp=lx[x]+ly[y]-Map[x][y]; if(tmp==0) { visy[y]=1; if(linker[y]==-1 || DFS(linker[y])) { linker[y]=x; return true; } } else { slack[y]=min(slack[y],tmp); } } return false; } int KM() { int i,j; memset(ly,0,sizeof(ly));//初始化可行标杆ly memset(linker,-1,sizeof(linker)); for(i=1; i<=n; i++)//初始化可行标杆lx { lx[i]=-INF; for(j=1; j<=n; j++) lx[i]=max(lx[i],Map[i][j]); } for(int x=1; x<=n; x++) { for(i=1; i<=n; i++) slack[i]=INF; while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(DFS(x))//若可加入直接跳出 break; int d=INF; for(i=1; i<=n; i++) { if(!visy[i]) d=min(slack[i],d);//否则根据贪心的思想,找到最小的可行标杆修改值 } for(i=1; i<=n; i++)//修改lx标杆 { if(visx[i]) lx[i]-=d; } for(i=1; i<=n; i++)//修改ly标杆 { if(visy[i]) ly[i]+=d; else slack[i]-=d;//slack由lx[x]+ly[y]-Map[x][y]而来;ly中未标记过的值不变,lx中已标记过的值减少,则slack应减少 } } } int ans=0; for(i=1;i<=n;i++) { ans+=Map[linker[i]][i]; } return ans; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) scanf("%d",&Map[i][j]); } int ans=KM(); printf("%d\n",ans); } return 0; }
时间: 2024-10-22 13:55:26