题意就不解释了。。
咋一看,似乎没什么头绪。
但是,我们可能会注意环串
的环
这个字。
环的概念一般在图论中出现,所以这里显然就用到了图的思想。
建图
我们可以把每个字符串的前两个和后两个字符作为顶点,将字符串看作边,那么最多有\(26^2\)个点,边的长度即为字符串长度。
注意我们要忽略长度为\(1\)的字符串。
然后我们又会注意到:平均长度最长
,所以我们很容易想到二分。
二分
我们假设答案为\(mid \in (0,1000)\),下界\(lb=-1\),上界\(rb=1001\)。
开始二分!
我们_将所有边权都_\(-mid\),再跑一边
SPFA
,看看现在的图中是否有正环
,若有,说明答案可以再增大,\(lb=mid\);否则,\(rb=mid\)。
- 最后,\(mid\)即为答案,若\(mid<0\),则无解。
\[\text{MainCodeHere}\]
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int N=26*26+1;
const double Eps=1e-3;
vector<int> G[N];
vector<int> W[N];
char input[1001];
int m;
double dis[N];
bool in[N];
int cnt[N];
inline bool Spfa(double test)
{
register int i;
queue<int>Q;
for(i=0;i<N;i++)
dis[i]=0,in[i]=1,cnt[i]=1,Q.push(i);
while(!Q.empty())
{
int now=Q.front();Q.pop();
in[now]=0;
if(cnt[now]>N)return 1;//有正环
for(i=0;i<G[now].size() ;i++)
{
if(dis[G[now][i]]>dis[now]-(W[now][i]-test))
{// 由于是正环,所以取相反数,注意边权-mid。
dis[G[now][i]]=dis[now]-(W[now][i]-test);
if(!in[G[now][i]])
{
Q.push(G[now][i]);
in[G[now][i]]=1;
cnt[G[now][i]]++;
}
}
}
}
return 0;
}
inline double Binary_Search()
{
double lb=-1,rb=1001,mid;
while(rb-lb>Eps)
{
mid=(lb+rb)/2;
if(Spfa(mid))lb=mid;//若有正环,答案小了。
else rb=mid;
}
return mid;
}
int main()
{
while(scanf("%d",&m)!=EOF)
{
if(!m)return 0;
register int i;
for(i=0;i<N;i++)
{
G[i].clear();
W[i].clear();
}
for(i=1;i<=m;i++)
{
scanf("%s",input);
int len=strlen(input);
if(len<2)continue;
int u=(input[0]-'a')*26+input[1]-'a';
int v=(input[len-2]-'a')*26+input[len-1]-'a';
//注意此处要-'a'。
G[u].push_back(v);
W[u].push_back(len);
}
double result=Binary_Search();//二分答案
if(result>0)printf("%.2lf\n",result);
else puts("No solution.");
}
}