km算法
今天花了些时间学了下km算法 看了下代码有点大概思路,还是要多做题;
KM算法求最小权二分匹配,模板题,构图很简单,直接把人当作左边的点,房子当作右边的点,
两者之间的曼哈顿距离当作权值即可。第一次搞带权二分匹配的题,就是用KM算法求最小权的时候要加个处,由于KM求的是最大权,
所以在套模板之前把权值都取下相反值最后再把KM算法求出来的最大权值取反即可。
Kuhn-Munkras算法流程:
(1)初始化可行顶标的值
(2)用匈牙利算法寻找完备匹配
(3)若未找到完备匹配则修改可行顶标的值
(4)重复(2)(3)直到找到相等子图的完备匹配为止
引用:
KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。
设顶点Xi的顶标为A[i],顶点Yi的顶标为B [i],顶点Xi与Yj之间的边权为w[i,j]。在算法执行过程中的任一时刻,对于任一条边(i,j),A[i]+B[j]>=w[i,j]始终 成立。
KM算法的正确性基于以下定理:
若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。
这个定理是显然的。因为对于二分图的任意一个匹配,如果它包含于相等子图,那么它的边权和等于所有顶点的顶标和;
如果它有的边不包含于相等子图,那么它的边权和小于所有顶点的顶标和。所以相等子图的完备匹配一定是二分图的最大权匹配。
初始时为了使A[i]+B[j]>=w[i,j]恒成立,令A[i]为所有与顶点Xi关联的边的最大权,B[j]=0。如果当前的相等子图没有完备匹配,就按下面的方法修改顶标以使扩大相等子图,
直到相等子图具有完备匹配为止。
我们求当前相等子图的完备匹配失败了,是因为对于某个X顶点,我们找不到一条从它出发的交错路。
这时我们获得了一棵交错树,它的叶子结点全部是X顶点。现在我们把交错树中X顶点的顶标全都减小某个值d,Y顶点的顶标全都增加同一个值d,那么我们会发现:
两端都在交错树中的边(i,j),A[i]+B[j]的值没有变化。也就是说,它原来属于相等子图,现在仍属于相等子图。
两端都不在交错树中的边(i,j),A[i]和B[j]都没有变化。也就是说,它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图。
X端不在交错树中,Y端在交错树中的边(i,j),它的A[i]+B[j]的值有所增大。它原来不属于相等子图,现在仍不属于相等子图。
X端在交错树中,Y端不在交错树中的边(i,j),它的A[i]+B[j]的值有所减小。也就说,它原来不属于相等子图,现在可能进入了相等子图,因而使相等子图得到了扩大。
现在的问题就是求d值了。为了使A[i]+B[j]>=w[i,j]始终成立,且至少有一条边进入相等子图,d应该等于min{A[i]+B[j]-w[i,j]|Xi在交错树中,Yi不在交错树中}。
以上就是KM算法的基本思路。但是朴素的实现方法,时间复杂度为O(n4)——需要找O(n)次增广路,每次增广最多需要修改O(n)次顶 标,每次修改顶标时由于要枚举边来求d值,
复杂度为O(n2)。实际上KM算法的复杂度是可以做到O(n3)的。我们给每个Y顶点一个“松弛量”函数 slack,每次开始找增广路时初始化为无穷大。
在寻找增广路的过程中,检查边(i,j)时,如果它不在相等子图中,则让slack[j]变成原值与A [i]+B[j]-w[i,j]的较小值。
这样,在修改顶标时,取所有不在交错树中的Y顶点的slack值中的最小值作为d值即可。但还要注意一点:修改 顶标后,要把所有的slack值都减去d。
最后把值加起来就ok。
1 //poj2195 km算法 2 #include<stdio.h> 3 #include<string.h> 4 #include<stdlib.h> 5 #define INF 99999999 6 char map[103][103]; 7 int n,m,g[103][103],visr[103],visl[103],pr[103],pl[103],match[103],num,slack[103]; 8 struct node 9 { 10 int x; 11 int y; 12 }mm[103],hh[103]; 13 int dfs(int u) 14 { 15 int i,j,val; 16 visl[u]=1; 17 for(i=0;i<num;i++) 18 { 19 if(!visr[i]) 20 { 21 val=pl[u]+pr[i]-g[u][i]; 22 if(val==0)//即满足匹配条件 23 { 24 visr[i]=1; 25 if(match[i]==-1||dfs(match[i])) 26 { 27 match[i]=u; 28 return 1; 29 } 30 } 31 if(val>0&&val<slack[i]) 32 slack[i]=val; 33 } 34 } 35 return 0; 36 } 37 int km() 38 { 39 int i,j,res,d; 40 res=0; 41 memset(pr,0,sizeof(pr));//右边的值为0 42 for(i=0;i<num;i++)//左边的值为INF 43 pl[i]=INF; 44 memset(match,-1,sizeof(match));//匈牙利算法 45 for(i=0;i<num;i++) 46 { 47 48 for(j=0;j<num;j++)//辅助数组slack[]初始为无穷大 49 slack[j]=INF; 50 51 while(1)//死循环知道有满足的匹配为止 52 { 53 memset(visl,0,sizeof(visl)); 54 memset(visr,0,sizeof(visr)); 55 if(dfs(i))break;//匹配成功结束 56 d=INF; 57 for(j=0;j<num;j++) 58 if(!visr[j]&&d>slack[j])//找到一个改进量dx,dx=min{Li+Lj-wi,j}(i∈S,j不∈T) 59 d=slack[j]; 60 //Li=Li-dx (i∈S) 61 for(j=0;j<num;j++) //Li=Li+dx (i∈T) 62 { //重复以上过程,不断的调整L,直到求出完备匹配为止。 63 if(visl[j]) 64 pl[j]-=d; 65 if(visr[j]) 66 pr[j]+=d; 67 } 68 } 69 } 70 for(j=0;j<num;j++) 71 res+=g[match[j]][j]; 72 return res; 73 } 74 int main() 75 { 76 int i,j; 77 while(scanf("%d%d",&n,&m)!=EOF) 78 { 79 if(!n&&!m)break; 80 81 for(i=0;i<n;i++) 82 scanf("%s",map[i]); 83 84 int numm,numh; 85 numm=numh=0; 86 for(i=0;i<n;i++) 87 { 88 for(j=0;j<m;j++) 89 { 90 if(map[i][j]==‘m‘)//记录人所在的坐标 91 { 92 mm[numm].x=i;mm[numm++].y=j; 93 } 94 if(map[i][j]==‘H‘)//记录房子所在的坐标 95 { 96 hh[numh].x=i;hh[numh++].y=j; 97 } 98 } 99 } 100 //建图 101 num=numm; 102 for(i=0;i<numm;i++) 103 { 104 for(j=0;j<numm;j++) 105 { 106 g[i][j]=-(abs(hh[i].x-mm[j].x)+abs(hh[i].y-mm[j].y));//任何人和任何房子间的距离 107 } 108 } 109 /*for(i=0;i<num;i++) 110 { 111 for(j=0;j<num;j++) 112 printf("%d ",g[i][j]); 113 printf("\n"); 114 }*/ 115 int ans=km(); 116 printf("%d\n",-ans); 117 } 118 }
1 //hdu2255 竟然700+ms 2 #include<stdio.h> 3 #include<string.h> 4 #define INF 99999999 5 #define maxn 303 6 int map[maxn][maxn],match[maxn],visl[maxn],visr[maxn],pr[maxn],pl[maxn],slack[maxn]; 7 int n,m; 8 int dfs(int u) 9 { 10 int i,j,val; 11 visl[u]=1; 12 for(i=1;i<=n;i++) 13 { 14 if(!visr[i]) 15 { 16 val=pl[u]+pr[i]-map[u][i]; 17 if(val==0) 18 { 19 visr[i]=1; 20 if(match[i]==-1||dfs(match[i])) 21 { 22 match[i]=u; 23 return 1; 24 } 25 } 26 if(val>0&&val<slack[i]) 27 slack[i]=val; 28 } 29 } 30 return 0; 31 } 32 int km() 33 { 34 int i,j,res,d; 35 res=0; 36 memset(pr,0,sizeof(pr)); 37 for(i=1;i<=n;i++) 38 pl[i]=INF; 39 memset(match,-1,sizeof(match)); 40 for(i=1;i<=n;i++) 41 { 42 for(j=1;j<=n;j++) 43 slack[j]=INF; 44 while(1) 45 { 46 memset(visl,0,sizeof(visl)); 47 memset(visr,0,sizeof(visr)); 48 if(dfs(i))break; 49 d=INF; 50 for(j=1;j<=n;j++) 51 { 52 if(!visr[j]&&d>slack[j]) 53 d=slack[j]; 54 } 55 for(j=1;j<=n;j++) 56 { 57 if(visr[j]) 58 pr[j]+=d; 59 if(visl[j]) 60 pl[j]-=d; 61 } 62 } 63 } 64 for(i=1;i<=n;i++) 65 res+=map[match[i]][i]; 66 return res; 67 } 68 int main() 69 { 70 int i,j; 71 while(scanf("%d",&n)!=EOF) 72 { 73 for(i=0;i<=n;i++) 74 for(j=0;j<=n;j++) 75 { 76 if(i==j)map[i][j]=0; 77 else map[i][j]=INF; 78 } 79 for(i=1;i<=n;i++) 80 { 81 for(j=1;j<=n;j++) 82 { 83 int z; 84 scanf("%d",&z); 85 map[i][j]=z; 86 } 87 } 88 int ans=km(); 89 printf("%d\n",ans); 90 } 91 }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。