HDU3718 Similarity KM
原文链接http://www.cnblogs.com/zhouzhendong/p/8284763.html
题目传送门 - HDU3718
题意概括
直接描述输入吧
首先一个T(T<15),表示数据组数。
每组数据,首先三个数:len,k,m,分别表示接下来要读入的字符串的长度、每一个字符串中出现的不同字母个种类数、询问的字符串数。(len<=10000)(k<=26)(m<30)
然后一行一个标准串。(长度为len)
然后m行,每行一个询问串。(长度为len)
对于询问串,每一种字母可以对应一种字母,问在最优方案下,使得对应万之后满足询问串与标准串的对应位相同的位的个数除以len的值。
完(kou)美(hu)
题解
我想骂人。
容我说点难听的话。
这题的样例数据简直就是垃圾。
第46行的j++写成了i++居然过了样例然后实测无限TLE?
言归正传。
我们考虑每一个字母变成另一个字母得到的贡献,然后在这两个字母之间连一条边,边权为贡献。
然后貌似就是裸的KM了。
OK水题。
代码
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; const int INF=1e9+7; const int N=30,L=10005; int T,len,n,m,g[N][N]; char cor[L],now[L]; int ex[N],ey[N],minadd[N],match[N]; bool visx[N],visy[N]; bool Match(int x){ visx[x]=1; for (int i=1;i<=n;i++) if (!visy[i]){ int add=ex[x]+ey[i]-g[x][i]; if (!add){ visy[i]=1; if (!match[i]||Match(match[i])){ match[i]=x; return 1; } } else minadd[i]=min(minadd[i],add); } return 0; } int KM(){ memset(match,0,sizeof match); memset(ex,0,sizeof ex); memset(ey,0,sizeof ey); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) ex[i]=max(ex[i],g[i][j]); for (int i=1;i<=n;i++){ for (int j=1;j<=n;j++) minadd[j]=INF; while (1){ memset(visx,0,sizeof visx); memset(visy,0,sizeof visy); if (Match(i)) break; int d=INF; for (int j=1;j<=n;j++) if (!visy[j]) d=min(d,minadd[j]); for (int j=1;j<=n;j++){ if (visx[j]) ex[j]-=d; if (visy[j]) ey[j]+=d; else minadd[j]-=d; } } } int ans=0; for (int i=1;i<=n;i++) ans+=g[match[i]][i]; return ans; } void readstr(char s[]){ char ch[5]; for (int i=1;i<=len;i++){ scanf("%s",ch); s[i]=ch[0]; } } int main(){ scanf("%d",&T); while (T--){ scanf("%d%d%d",&len,&n,&m); n=26; readstr(cor); while (m--){ readstr(now); memset(g,0,sizeof g); for (int i=1;i<=len;i++) g[now[i]-'A'+1][cor[i]-'A'+1]++; printf("%.4lf\n",1.0*KM()/len); } } return 0; }