KM算法及其应用

  在二分图匹配中有最大匹配问题,使用匈牙利算法或者网络流相关算法解决,如果给每条边增加一个权值,求权值和最大的匹配方案就叫做最大权匹配问题。其实之前所说的最大匹配就是权值为1的最大权匹配。

  求最大权完备匹配常用的方法是Kuhn-Munkres算法(简称KM算法),其主要思想就是通过顶标将求最大权匹配问题转化为求解最大匹配问题。算法的大致思路是任意构造一个可行顶标(比如Y结点顶标为0,X结点的顶标为它出发所有边的最大权值),然后求相等子图的最大匹配,如果存在完美匹配,算法终止,否则修改顶标使得相等子图的边变多,有更大的机会存在完美匹配。

  下面以题为例,给出时间复杂度O(n4)的算法。

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3722

  1 /*
  2 问题 
  3 将任意的两个字符串进行匹配,使得匹配后权值和最大
  4 
  5 解题思路
  6 将任意的字符串的权值计算出来,使用KM算法即可。 
  7 */ 
  8 #include<cstdio>
  9 #include<cstring>
 10 #include<algorithm>
 11 using namespace std;
 12 
 13 const int maxn=210;
 14 int W[maxn][maxn],n;
 15 char s[maxn][1100];
 16 int lx[maxn],ly[maxn];
 17 int left[maxn];
 18 bool S[maxn],T[maxn];
 19 int len[maxn];
 20 
 21 void presolve();
 22 int KM();
 23 bool match(int i);
 24 void update();
 25 
 26 int main()
 27 {
 28     int i,j;
 29     while(scanf("%d",&n) != EOF)
 30     {
 31         for(i=1;i<=n;i++)
 32         {
 33             scanf("%s",s[i]);
 34             len[i]=strlen(s[i]);
 35         }
 36         presolve();
 37 
 38         printf("%d\n",KM());
 39     }
 40 }
 41 
 42 bool match(int i)
 43 {
 44     S[i]=true;
 45     for(int j=1;j<=n;j++) if(lx[i]+ly[j] == W[i][j] && !T[j]){
 46         T[j]=true;
 47         if(!left[j] || match(left[j])){
 48             left[j]=i;
 49             return true;
 50         }
 51     }
 52     return false;
 53 }
 54 
 55 void update()
 56 {
 57     int a= 1<<30;
 58     for(int i=1; i<=n; i++){
 59         if(S[i]){
 60             for(int j=1;j<=n;j++){
 61                 if(!T[j]){
 62                     a = min(a,lx[i]+ly[j] - W[i][j]);
 63                 }
 64             }
 65         }
 66     }
 67     
 68     for(int i=1;i<=n;i++){
 69         if(S[i])    lx[i] -= a;
 70         if(T[i])    ly[i] += a;
 71     }
 72 }
 73 
 74 int KM()
 75 {
 76     for(int i=1;i<=n;i++){
 77         left[i] = lx[i] = ly[i] = 0;
 78         for(int j=1; j<=n; j++)
 79             lx[i]=max(lx[i],W[i][j]);    
 80     }
 81     
 82     for(int i=1; i<=n; i++){
 83         for(;;){
 84             for(int j=1;j<=n;j++){
 85                 S[j]=T[j]=0;
 86             }
 87             if(match(i)) break;
 88             else update();
 89         }
 90     }
 91     
 92     int ans=0;
 93     for(int i=1;i<=n;i++)
 94         ans += W[left[i]][i];
 95     return ans;
 96 }
 97 
 98 void presolve()
 99 {
100     int i,j,k,p1,p2,cnt;
101     for(i=1;i<=n;i++)
102     {
103         for(j=1;j<=n;j++)
104         {
105             if(i==j)
106             {
107                 W[i][j]=0;
108                 continue ;
109             }
110             
111             cnt=0;
112             p1=len[i]-1;
113             p2=0;
114             while(1)
115             {
116                 if(s[i][p1]==s[j][p2]) cnt++;
117                 else break ;
118                 p1--,p2++;
119                 if(p1<0 || p2>=len[j]) break ;
120             }
121             W[i][j]=cnt;
122         }
123     }
124 }

 

posted @ 2018-05-17 22:44  Reqaw  阅读(687)  评论(0编辑  收藏  举报