hdu 3718 二分图最佳匹配 占坑待补

来源:互联网 发布:java switch 例子 编辑:程序博客网 时间:2024/05/20 17:07

题目:

Similarity

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2160    Accepted Submission(s): 868


Problem Description
When we were children, we were always asked to do the classification homework. For example, we were given words {Tiger, Panda, Potato, Dog, Tomato, Pea, Apple, Pear, Orange, Mango} and we were required to classify these words into three groups. As you know, the correct classification was {Tiger, Panda, Dog}, {Potato, Tomato, Pea} and {Apple, Pear, Orange, Mango}. We can represent this classification with a mapping sequence{A,A,B,A,B,B,C,C,C,C}, and it means Tiger, Panda, Dog belong to group A, Potato, Tomato, Pea are in the group B, and Apple, Pear, Orange, Mango are in the group C.
But the LABEL of group doesn't make sense and the LABEL is just used to indicate different groups. So the representations {P,P,O,P,O,O,Q,Q,Q,Q} and {E,E,F,E,F,F,W,W,W,W} are equivalent to the original mapping sequence. However, the representations {A,A,A,A,B,B,C,C,C,C} and
{D,D,D,D,D,D,G,G,G,G} are not equivalent.



The pupils in class submit their mapping sequences and the teacher should read and grade the homework. The teacher grades the homework by calculating the maximum similarity between pupils' mapping sequences and the answer sequence. The definition of similarity is as follow. 

Similarity(S, T) = sum(Si == Ti) / L 
L = Length(S) = Length(T), i = 1, 2,... L,
where sum(Si == Ti) indicates the total number of equal labels in corresponding positions. The maximum similarity means the maximum similarities between S and all equivalent sequences of T, where S is the answer and fixed. Now given all sequences submitted by pupils and the answer sequence, you should calculate the sequences' maximum similarity.
 

Input
The input contains multiple test cases. The first line is the total number of cases T (T < 15). The following are T blocks. Each block indicates a case. A case begins with three numbers n (0 < n < 10000), k (0 < k < 27), m (0 < m < 30), which are the total number of objects, groups, and students in the class. The next line consists of n labels and each label is in the range [A...Z]. You can assume that the number of different labels in the sequence is exactly k. This sequence represents the answer. The following are m lines, each line contains n labels and each label also is in the range [A...Z]. These m lines represent the m pupils' answer sequences. You can assume that the number of different labels in each sequence doesn't exceed k.
 

Output
For each test case, output m lines, each line is a floating number (Round to 4 digits after the decimal point). You should output the m answers in the order of the sequences appearance.
 

Sample Input
210 3 3A A B A B B C C C CF F E F E E D D D DX X X Y Y Y Y Z Z ZS T R S T R S T R S3 2 2A B AC D CF F E
 

Sample Output
1.00000.70000.50001.00000.6667
 

Author
LIN, Yue
 

Source
2010 Asia Chengdu Regional Contest
 

Recommend
zhouzeyong




需要先了解几个定义:

顶标:图的顶点的标号

完全二分图:是一种特殊的二分图,可以把中的顶点分成两个集合,使得第一个集合中的所有顶点都与第二个集合中的所有顶点相连

二分图的完备匹配:不含未匹配点的匹配 即二分图中所有点都有边相连的匹配

最优匹配:所有匹配边的权和最大的匹配方案

对于二分图的每条边都有一个权(非负),要求一种完备匹配方案,使得所有匹配边的权和最大,记做最优完备匹配。(特殊的,当所有边的权为1时,就是最大完备匹配问题)

可行顶标:L是一个关于结点的函数,L(x)是顶点x对应的顶标值。可行顶标对于图中的每条边(x,y)都有L(x)+L(y)>=w(x,y)
相等子图:只包含L(x)+L(y)=w(x,y)的边的子图

定理:如果一个相等子图中包含完备匹配,那么这个匹配就是最优匹配


[KM算法的几种变形]

1.  KM算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。
2.  KM算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备,可以有未匹配点)该如何办?依然很简单,把不存在的边权值赋为0。
3.  KM算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。至于精度问题则没有更好的办法了。


参考资料:

http://cse.seu.edu.cn/PersonalPage/hxue/downloadTalk/download5/%E4%BA%8C%E5%88%86%E5%9B%BE%E7%9A%84%E5%8C%B9%E9%85%8D%E9%97%AE%E9%A2%98-%E5%AD%94%E5%AD%90%E6%98%8E%EF%BC%8C%E5%90%B4%E6%A5%9A%E6%98%8A%EF%BC%8C%E8%8B%8F%E8%B5%9B.pdf

http://cse.seu.edu.cn/PersonalPage/hxue/downloadTalk/download5/%E4%BA%8C%E5%88%86%E5%9B%BE%E7%9A%84%E6%9C%80%E4%BD%B3%E5%8C%B9%E9%85%8D%E9%97%AE%E9%A2%98-%E5%90%95%E5%A4%A9%E7%BF%8E.pdf

https://www.byvoid.com/zhs/blog/match-km

http://111qqz.com/2016/06/hdu-2255-%E5%A5%94%E5%B0%8F%E5%BA%B7%E8%B5%9A%E5%A4%A7%E9%92%B1-%E4%BA%8C%E5%88%86%E5%9B%BE%E6%9C%80%E4%BD%B3%E5%8C%B9%E9%85%8D%EF%BC%8Ckm%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF%E9%A2%98%EF%BC%89/


本题建图方法:






代码:

不清楚原理的情况下写出的一份貌合神离的辣鸡代码

#include<bits/stdc++.h>using namespace std;const int INF=0x3f3f3f3f;const int N=30;int nx,ny;//两边点数int g[N][N];//图 下标从0开始int linker[N],lx[N],ly[N];//y中各点匹配状态,两边顶标int slack[N];bool visx[N],visy[N];int t,n,m,k;bool dfs(int x){    visx[x]=true;    for(int y=0;y<ny;++y){        if(visy[y]) continue;        int tmp=lx[x]+ly[y]-g[x][y];        if(tmp==0){            visy[y]=true;            if(linker[y]==-1||dfs(linker[y])){                linker[y]=x;                return true;            }        }        else if(slack[y]>tmp) slack[y]=tmp;    }    return false;}int KM(){    memset(linker,-1,sizeof(linker));    memset(ly,0,sizeof(ly));    for(int i=0;i<nx;++i){        lx[i]=-INF;//////lx[i]=;        for(int j=0;j<k;++j){            if(g[i][j]>lx[i]) lx[i]=g[i][j];        }    }    for(int x=0;x<nx;++x){        for(int i=0;i<ny;++i) slack[i]=INF;////        while(1){            memset(visx,false,sizeof(visx));            memset(visy,false,sizeof(visy));            if(dfs(x)) break;            int d=INF;            for(int i=0;i<ny;++i){                if(!visy[i] && d>slack[i]) d=slack[i];            }            for(int i=0;i<nx;++i){                if(visx[i]) lx[i]-=d;            }            for(int i=0;i<ny;++i){                if(visy[i]) ly[i]+=d;                else slack[i]-=d;            }        }    }    int res=0;    for(int i=0;i<ny;++i){        if(linker[i]!=-1) res+=g[linker[i]][i];    }    return res;}int main(){//327MS2012K    char c[3];    scanf("%d",&t);    while(t--){        string s;        scanf("%d%d%d",&n,&k,&m);        for(int i=0;i<n;++i){            scanf("%1s",c);            s+=c[0];        }        //cout<<s<<endl;        memset(g,0,sizeof(g));        for(int i=0;i<m;++i){            for(int j=0;j<n;++j){                scanf("%1s",c);                g[s[j]-'A'][c[0]-'A']++;            }            nx=26; ny=26;            int ans=KM();            //cout<<ans<<endl;            printf("%.4f\n",1.0*ans/(n*1.0));        }    }    return 0;}


111qqz的代码:

#include<bits/stdc++.h>using namespace std;#define ms(a,x) memset(a,x,sizeof(a))const int inf = 0x3f3f3f3f;const int N=1E4+7;int n,k,m;char a[35][N];int w[35][35];map<char,int>mp1,mp2;int tot1,tot2;int lx[35],ly[35];bool visx[35],visy[35];int link[35];int slk[35];bool find( int u){    visx[u] = true;    for ( int v = 1 ; v <= k ; v++){        if (visy[v]) continue;        int tmp = lx[u] + ly[v] - w[u][v];        if (tmp==0){            visy[v] = true;            if (link[v]==-1||find(link[v])){            link[v] = u;            return true;            }        }else if (tmp<slk[v]) slk[v] = tmp;    }    return false;}int KM(){    ms(link,-1);    ms(lx,0);    ms(ly,0);    for ( int i = 1 ; i <= tot2 ; i++)for ( int j = 1 ; j <= k ; j++)    lx[i] = max(lx[i],w[i][j]);    for ( int i = 1 ; i <= tot2 ; i++){        ms(slk,0x3f);        while (1){            ms(visx,false);            ms(visy,false);            if (find(i)) break;            int d = inf;            for ( int j = 1 ;j <= k ; j++)            if (!visy[j]&&slk[j]<d) d = slk[j];            for ( int j = 1 ; j <= tot2 ; j++)            if (visx[j]) lx[j]-=d;            for ( int j = 1; j <= k ; j++)            if (visy[j]) ly[j]+=d; else slk[j]-=d;        }    }    int res = 0 ;    for ( int i = 1 ;  i <= k  ; i++)if (link[i]>-1) res += w[link[i]][i];    return res;}int main(){//218MS2032Kint T;cin>>T;while (T--){    ms(w,0);    mp1.clear();    mp2.clear();    tot1 = 0 ;    tot2 = 0 ;    scanf("%d%d%d",&n,&k,&m);    getchar();    for ( int i = 0 ; i <= m ; i++){            for ( int j = 1 ; j <= n ; j++)                scanf(" %c",&a[i][j]);            getchar();    }    for ( int i = 1 ; i <= n ; i++) if (!mp1[a[0][i]]) mp1[a[0][i]]=++tot1;    for ( int i = 1;  i <= m ; i++){ms(w,0);tot2 = 0 ;mp2.clear();for ( int j = 1 ; j <= n ; j++) if (!mp2[a[i][j]]) mp2[a[i][j]] = ++tot2;for ( int j =  1; j <= n ; j++) {    int u = mp1[a[0][j]];    int v = mp2[a[i][j]];    w[v][u]++;}int ret = KM();double ans;ans = ret*1.0/(n*1.0);printf("%.4f\n",ans);    }}    return 0;}


还是不能着急功近利 一步一步来 学得慢没关系,每个人基础不一样,智商不一样,学起来也是不一样的速度。关键是学扎实,理解清楚,才能以不变应万变