Word Rings
题目描述
给出若干字符串,如果A的末2个字符和B的首2个字符相同,那么称A和B相连,求从给定的字符串中找出一条环串使串的平均长度最短。
思路
首先我们考虑建图,显然,以字符串为点的图难以实现,而两个字符最多只有26×26个节点,那么字符串就是连接两个节点的边,其边权即为串的长度。接下来我们考虑如何求串的平均长度。
假设串的平均长度为average,那么我们有等式:
(s1-average)+(s2-average)...(sn-average)=0。
由此我们可以知道这个average具有单调性,若把每条边的边权减去average后存在一个正环,那么就把二分的答案减小。
不过如果用常规的spfa求环,很可能会T,被卡到上界。我们考虑这里并不需要每个点的最短路,因此完全可以不用bfs,而用dfs。而为何能用dfs,因为查找正环和最短路不一样,我们不用正确的最短路,而是一个点的被更新次数,并且dfs很可能通过访问从一个点出发重新到达一个点,因此效率相比较bfs大大提高了。我们还可以进行优化,如果dis[x]>最大边权*边数,显然就出现了正环。
代码
#include <bits/stdc++.h> using namespace std; const int N=1e5+10; const double eps=1e-4; int nxt[N],head[N],to[N],vis[N],tot; double dis[N],w[N],maxe; int f,alpha[N]; char s[1100]; void add_edge(int x,int y,double v) { nxt[++tot]=head[x]; head[x]=tot; to[tot]=y; w[tot]=v; } void spfa(int u,int h,double aver) { if(f)return ; vis[u]=h; for(int i=head[u];~i;i=nxt[i]) { int v=to[i]; if(dis[u]+w[i]-aver>dis[v]) { dis[v]=dis[u]+w[i]-aver; if(dis[v]>maxe){f=1;return ;}//最大边权 if(!vis[v])spfa(v,h,aver); if(vis[v]==h){f=1;return ;}//已在dfs中访问过 } } vis[u]=0; } bool check(double aver) { memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); f=0; for(int i=1;i<=tot;i++) { spfa(i,i,aver); if(f)break ; } return f; } int main() { int n,idx; while(~scanf("%d",&n)) { tot=0;maxe=0;idx=0; memset(alpha,0,sizeof(alpha)); memset(head,-1,sizeof(head)); if(n==0)break ; for(int i=1;i<=n;i++) { scanf(" %s",s); int len=strlen(s); maxe=max(maxe,(double)len); int x=(s[0]-'a')*26+s[1]-'a'; int y=(s[len-2]-'a')*26+s[len-1]-'a'; if(!alpha[x])alpha[x]=++idx; int id1=alpha[x]; if(!alpha[y])alpha[y]=++idx; int id2=alpha[y]; add_edge(id1,id2,len); } maxe=maxe*n; double l=0,r=1000,ans=-1; while(r-l>eps) { // cout<<r<<' '<<l<<endl; double mid=(l+r)/2; if(check(mid))ans=mid,l=mid; else r=mid; } if(ans!=-1)printf("%.3lf\n",ans); else printf("No solution\n"); } return 0; }