# KM算法入门

KM算法的基本概念：

http://baike.baidu.com/view/739278.htm

http://baike.baidu.com/view/501092.htm

3 4

0 0 2

0 1 6

1  1 7

2 1  14

2  2  3

#define M 505#define inf 0x3fffffffbool sx[M], sy[M];int match[M], w[M][M], n, m, d, lx[M], ly[M];//n：左集元素个数； m：右集元素个数void init (){    memset (w, 0, sizeof(w));    //不一定要，求最小值一般要初始化为负无穷！}bool dfs (int u){    int v; sx[u] = true;    for (v = 0; v < m; v++)    {        if (!sy[v] && lx[u]+ly[v]==w[u][v])        {            sy[v] = true;            if (match[v] == -1 || dfs (match[v]))            {                match[v] = u;                return true;            }        }    }    return false;}int KM (){    int i, j, k, sum = 0;    memset (ly, 0, sizeof(ly));    for (i = 0; i < n; i++)    {        lx[i] = -inf;        for (j = 0; j < m; j++)            if (lx[i] < w[i][j])                lx[i] = w[i][j];    }    memset (match, -1, sizeof(match));    for (i = 0; i < n; i++)    {        while (1)        {            memset (sx, false, sizeof(sx));            memset (sy, false, sizeof(sy));            if (dfs (i))                break;            d = inf;            for (j = 0; j < n; j++)                if (sx[j])                    for (k = 0; k < m; k++)                        if (!sy[k])                            d = min (d, lx[j]+ly[k]-w[j][k]);            if (d == inf)    //找不到完美匹配                return -1;            for (j = 0; j < n; j++)                if (sx[j])                    lx[j] -= d;            for (j = 0; j < m; j++)                if (sy[j])                    ly[j] += d;        }    }    for (i = 0; i < m; i++)        if (match[i] > -1)            sum += w[match[i]][i];    return sum;}

/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>//赤裸裸的模板啊。。const int maxn = 101;const int INF = (1<<31)-1;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];int nx,ny;bool find(int x){    visx[x] = true;    for(int y = 0; y < ny; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 0; i < nx; i++)        for(j = 0,lx[i] = -INF; j < ny; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    for(int x = 0; x < nx; x++)    {        for(i = 0; i < ny; i++)            slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出                break;            int d = INF;            for(i = 0; i < ny; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 0; i < nx; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 0; i < ny; i++)            {                if(visy[i])                     ly[i] += d;                else                     slack[i] -= d;            }        }    }    int result = 0;    for(i = 0; i < ny; i++)    if(linky[i]>-1)        result += w[linky[i]][i];    return result;}int main(){   // freopen("g:/1.txt","r",stdin);    while(true)    {        scanf("%d%d",&nx,&ny);        int a,b,c;        while(scanf("%d%d%d",&a,&b,&c),a+b+c)        {            w[a][b]=c;        }        printf("%d\n",KM());        break;    }    return 0;}

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

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>//赤裸裸的模板啊。。const int maxn = 301;const int INF = (1<<31)-1;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];int n;bool find(int x){    visx[x] = true;    for(int y = 1; y <=n; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <=n; i++)    {        lx[i] = -INF;         for(j = 1; j <=n; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=n; x++)    {        for(i = 1; i <=n; i++)            slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出                break;            int d = INF;            for(i = 1; i <=n; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <=n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1; i <=n; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }    int result = 0;    for(i = 1; i <=n; i++)    if(linky[i]>-1)        result += w[linky[i]][i];    return result;}int main(){   // freopen("g:/1.txt","r",stdin);    while(scanf("%d",&n)==1)    {        int cost;        for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)        {            scanf("%d",&cost);            w[i][j]=cost;        }        printf("%d\n",KM());    }    return 0;}

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

用 w[i][j] = -w[i][j]建图再套模板 求最大值   输出【-sum】

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int fx,fy;const int maxn = 301;const int INF = 0xffffff;struct node{    int x,y;}sx[maxn],sy[maxn];int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <=fy; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <=fx; i++)    {        lx[i] = -INF;         for(j = 1; j <=fy; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=fx; x++)    {        for(i = 1; i <=fy; i++)            slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出                break;            int d = INF;            for(i = 1; i <=fy; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <=fx; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1; i <=fy; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }    int result = 0;    for(i = 1; i <=fy; i++)    if(linky[i]>-1)        result += w[linky[i]][i];    return result;}int main(){   // freopen("g:/1.txt","r",stdin);    int n,m;    while(scanf("%d%d",&n,&m),n+m)    {        char cost[maxn];        int i,j;fx=0,fy=0;        for( i=0;i<n;i++)        {             scanf("%s",cost);             for(int j=0;j<m;j++)             if(cost[j]=='m')             sx[++fx].x=i,sx[fx].y=j;             else if(cost[j]=='H')             sy[++fy].x=i,sy[fy].y=j;        }        for(i=1;i<=fx;i++)        {           for(j=1;j<=fy;j++)           {              int xx=fabs(sx[i].y-sy[j].y)+fabs(sx[i].x-sy[j].x);              w[i][j]=-xx;           }         }        printf("%d\n",-KM());    }    return 0;}

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m;const int maxn = 105;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <=n; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <=n; i++)    {        lx[i] = -INF;         for(j = 1; j <=n; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=n; x++)    {        for(i = 1; i <=n; i++)            slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出                break;            int d = INF;            for(i = 1; i <=n; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <=n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1; i <=n; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }    int result = 0 ;    for(i = 1; i <=n; i++)    {        if(linky[i]==-1||w[linky[i]][i]==-INF)        return 1;        else    result += w[linky[i]][i];    }    return result;}int main(){   // freopen("g:/1.txt","r",stdin);    while(scanf("%d%d",&n,&m)==2)    {        for(int i=1;i<=n;i++)        {            for(int j=1;j<=n;j++)            w[i][j]=-INF;        }        for(int i=1;i<=m;i++)        {           int a,b,c;           scanf("%d%d%d",&a,&b,&c);           if(-c>w[a][b])           w[a][b]=-c;        }        printf("%d\n",-KM());    }    return 0;}

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m;const int maxn = 205;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <=n; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <=n; i++)    {        lx[i] = -INF;         for(j = 1; j <=n; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=n; x++)    {        for(i = 1; i <=n; i++)            slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出                break;            int d = INF;            for(i = 1; i <=n; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <=n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1; i <=n; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }    int result = 0 ;    for(i = 1; i <=n; i++)    {        if(linky[i]==-1||w[linky[i]][i]==-INF)        return 1;        else    result += w[linky[i]][i];    }    return result;}int main(){   // freopen("g:/1.txt","r",stdin);    int cas; scanf("%d",&cas);    while(cas--)    {       scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++)        {            for(int j=1;j<=n;j++)            w[i][j]=-INF;        }        for(int i=1;i<=m;i++)        {           int a,b,c;           scanf("%d%d%d",&a,&b,&c);           if(-c>w[a][b])           w[a][b]=-c;        }        printf("%d\n",-KM());    }    return 0;}

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

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m;const int maxn = 1005;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <=n; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <=n; i++)    {         lx[i] = -INF;         for(j = 1; j <=n; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=n; x++)    {        for(i = 1; i <=n; i++)        slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出            break;            int d = INF;            for(i = 1; i <=n; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <=n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1; i <=n; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }}int main(){   // freopen("g:/1.txt","r",stdin);    int cas,p=1; scanf("%d",&cas);    while(p<=cas)    {        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++)        {            for(int j=1;j<=n;j++)            w[i][j]=-INF;        }        for(int i=1;i<=m;i++)        {           int a,b,c;           scanf("%d%d%d",&a,&b,&c);           if(-c>w[a][b])           w[a][b]=w[b][a]=-c;        }        KM();      int result = 0 ,flag=0;      for(int i = 1; i <=n; i++)      {        if(linky[i]==-1||w[linky[i]][i]==-INF)             {                flag=1;break;             }        else    result += w[linky[i]][i];       }        if(flag) printf("Case %d: NO\n",p++);        else printf("Case %d: %d\n",p++,-result);    }    return 0;}

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

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m,v;const int maxn = 1005;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 0; y < m; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}int KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 0; i <n; i++)    {         lx[i] = -INF;         for(j = 0; j <m; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 0; x <n; x++)    {        for(i = 0; i < m; i++)        slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出            break;            int d = INF;            for(i = 0; i < m; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 0; i < n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 0 ; i < m; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }}int main(){   // freopen("g:/1.txt","r",stdin);       int p=1;       while(scanf("%d%d%d",&n,&m,&v)==3)       {         for(int i=0;i<n;i++)         {            for(int j=0;j<m;j++)            w[i][j]=-INF;          }        for(int i=0;i<v;i++)        {           int a,b,c;           scanf("%d%d%d",&a,&b,&c);           if(c < 0) continue ;           w[a][b]=c;        }        if(v<=0) { printf("Case %d: -1\n",p++);continue;}        KM();        int result = 0 ,flag=0;        for(int i = 0; i < m; i++)         {         if(linky[i]>-1&&w[linky[i]][i]!=INF)         { result += w[linky[i]][i]; flag++;}         }         if(flag<n)  result=-1;         printf("Case %d: %d\n",p++,result);      }    return 0;}

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

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m,v;const int maxn = 1005;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <= m; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}void KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i <= n; i++)    {         lx[i] = -INF;         for(j = 1; j <=m; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <=n; x++)    {        for(i = 1; i <= m; i++)        slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出            break;            int d = INF;            for(i = 1; i <= m; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i <= n; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1 ; i <= m; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }}int main(){   // freopen("g:/1.txt","r",stdin);       while(scanf("%d%d",&n,&m)==2)       {          // memset(w,0,sizeof(w));         for(int i=1;i<=n;i++)            for(int j=1;j<=m;j++)            {                int  c;               scanf("%d",&c);               w[i][j]=c*100;            }        int  num=0;        for(int i=1;i<=n;i++)        {            int ss;            scanf("%d",&ss);            num+=w[i][ss];            w[i][ss]++;        }        KM();        int result = 0  ;        for(int i = 1; i <= m; i++)         {          if(linky[i]!=-1)              result += w[linky[i]][i];         }         printf("%d %d\n",n-result%100,result/100-num/100);      }    return 0;}

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

【字母的最大匹配值/n】 注意输入 scanf--%s   不要用 %c%*c

View Code
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*//*最大最小有一些地方不同。。*/#include <iostream>#include<cstring>#include<cstdio>#include<cmath>int n,m,v;const int maxn = 30;const int INF = 0xffffff;int w[maxn][maxn];int lx[maxn],ly[maxn]; //顶标int linky[maxn];int visx[maxn],visy[maxn];int slack[maxn];bool find(int x){    visx[x] = true;    for(int y = 1; y <27; y++)    {        if(visy[y])            continue;        int t = lx[x] + ly[y] - w[x][y];        if(t==0)        {            visy[y] = true;            if(linky[y]==-1 || find(linky[y]))            {                linky[y] = x;                return true;        //找到增广轨            }        }        else if(slack[y] > t)            slack[y] = t;    }    return false;                   //没有找到增广轨（说明顶点x没有对应的匹配，与完备匹配(相等子图的完备匹配)不符）}void KM()                //返回最优匹配的值{    int i,j;    memset(linky,-1,sizeof(linky));    memset(ly,0,sizeof(ly));    for(i = 1; i < 27; i++)    {         lx[i] = -INF;         for(j = 1; j <27; j++)            if(w[i][j] > lx[i])                lx[i] = w[i][j];    }    for(int x = 1; x <27; x++)    {        for(i = 1; i < 27; i++)        slack[i] = INF;        while(true)        {            memset(visx,0,sizeof(visx));            memset(visy,0,sizeof(visy));            if(find(x))                     //找到增广轨，退出            break;            int d = INF;            for(i = 1; i < 27; i++)          //没找到，对l做调整(这会增加相等子图的边)，重新找            {                if(!visy[i] && d > slack[i])                    d = slack[i];            }            for(i = 1; i < 27; i++)            {                if(visx[i])                    lx[i] -= d;            }            for(i = 1 ; i <27; i++)            {                if(visy[i])                     ly[i] += d;                else                    slack[i] -= d;            }        }    }}int main(){   // freopen("g:/1.txt","r",stdin);       char str[10005];       int t;scanf("%d",&t);       while(t--)       {           int v;           scanf("%d%d%d%*c",&n,&m,&v);           for(int i=0;i < n;i++)            {               char   c[5];               scanf("%s",c);               str[i]=c[0];            }         for(int i=0;i< v;i++)         {            memset(w,0,sizeof(w));            char ss[5];            for(int j=0;j < n;j++)            {             scanf("%s",&ss);             w[str[j]-'A'+1][ss[0]-'A'+1]++;            }           /* for(int kk=1;kk<=27;kk++)            {                for(int jj=1;jj<=27;jj++)                printf("%d",w[kk][jj]);                putchar('\n');            }*/            KM();            double  result = 0  ;            for(int i = 1; i < 27; i++)           {               if(linky[i]!=-1)              result += w[linky[i]][i];           }         printf("%.4f\n",result/n);        }       }    return 0;}

posted @ 2012-02-18 12:12 skyming 阅读(...) 评论(...) 编辑 收藏