传送门
这题要先巧妙的转化一下。
对于每个字符串,我们把头尾的两个小字符串对应的点连边,边权是这个字符串的长度。
这样最多会出现26*26个点。
这个时候就只用求出边权和跟边数的最大比值了。
这个显然01分数规划+spfa判环解决。
注意用dfs版的spfa要快一些。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 200005
using namespace std;
int n,first[N],id1,id2,cnt,tot,len;
bool in[N];
double dis[N];
char s[1005];
struct edge{int v,next,w;}e[N<<2];
inline int idx(char a,char b){return (a-'a')*26+b-'a'+1;}
inline void add(int u,int v,int w){e[++cnt].v=v,e[cnt].next=first[u],e[cnt].w=w,first[u]=cnt;}
inline bool check(double a,double b){return fabs(a-b)>=1e-10;}
inline bool spfa(int p,double mid){
in[p]=true;
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(dis[v]>dis[p]+mid-e[i].w){
dis[v]=dis[p]+mid-e[i].w;
if(in[v]||spfa(v,mid)){in[p]=false;return true;}
}
}
return in[p]=false;
}
inline bool judge(double mid){
memset(dis,0,sizeof(dis));
for(int i=1;i<=26*26;++i)if(spfa(i,mid))return true;
return false;
}
int main(){
while(scanf("%d",&n)&&n){
cnt=0,memset(first,0,sizeof(first));
for(int i=1;i<=n;++i){
scanf("%s",s),len=strlen(s);
if(len<2)continue;
id1=idx(s[0],s[1]),id2=idx(s[len-2],s[len-1]),add(id1,id2,len);
}
double l=0.0,r=1000.0;
while(check(l,r)){
double mid=(l+r)/2;
if(judge(mid))l=mid;
else r=mid;
}
if(l==0.0)puts("No solution.");
else printf("%.4lf\n",l);
}
return 0;
}