题目描述:
给你n个字符串。
如果一个字符串的最后两个字符和另一个字符串的开头两个字符相同的话,那么这两个字符串可以连接。
现在让你连接成一个环(当然,每个字符串只能用一次)。
这个环的平均长度是:环中所有的字符串长度之和 除以 字符串的个数。
输出 最长 的平均长度。
如果不存在,输出 无解。
解题报告:
建图:
单词:intercommunicational
那么就连接in节点 和 al节点,权值为单词的长度。
问题就转化成了:
在图中找环,环的边权相加 除以 边数 就是这个环的平均长度。
但是怎样找到最长的呢?
二分!
注意到:
枚举每一个平均长度mid值。
如果存在一个环,(E1+...+Ek)/k>=mid(其中k是边数,E1……Ek是各个边权),
那么正解比mid大,否则比mid小,这就是二分策略。
那么怎样知道是否存在(E1+...+Ek)/k>=mid 呢?
如下转化:(E1+...+Ek)>=mid*k
E1-mid + E2–mid + E3-mid + ... + Ek-mid >= 0
所以,把所有的边权改为Ei – mid,然后看是否存在正环就可以,存在就是满足条件。
源代码:
/*二分+SPFA判正环(有向图)*/
/*
(1):例intercommunicational则把in->al权值为20
*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN 700
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define INF 1e8
#define eps 1e-4
const int vn=26*26;
using namespace std;
struct edge
{
int u,v,w,next;
}E[2000000];
int Q[20000000];
int head[MAXN],ecnt;
double dis[MAXN];
bool vis[MAXN];
bool mark[MAXN];
int num[MAXN];
int N,Max,Min,scr;
int map[MAXN][MAXN];
void Insert(int u,int v,int w)
{
E[ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].next=head[u];
head[u]=ecnt++;
}
void Init()
{
int i,j,u,v,w;
char s[1005];
memset(head,-1,sizeof(head));ecnt=0;
memset(map,0,sizeof(map));
for(i=1;i<=N;i++)
{
scanf("%s",s);
w=strlen(s);
if(w<2) continue;
Max=max(Max,w);
Min=min(Min,w);
u=(s[0]-'a')*26+(s[1]-'a')+1;
v=(s[w-2]-'a')*26+(s[w-1]-'a')+1;
map[u][v]=max(map[u][v],w);//优化
}
for(i=1;i<=vn;i++)
{
for(j=1;j<=vn;j++)
{
if(map[i][j])
Insert(i,j,map[i][j]);
}
}
//关键,建一个超级源点保证原图联通(类似查分约束)
scr=0;
for(i=1;i<=vn;i++)
Insert(scr,i,0);
}
bool SPFA(int s,double mid)
{
int i,u,v;
double w;
memset(num,0,sizeof(num));
memset(vis,false,sizeof(vis));
for(i=1;i<=vn;i++)
dis[i]=-INF;
int Tail,Head;
Tail=Head=0;
Q[Tail++]=s;
vis[s]=true;
dis[s]=0;
num[s]=1;
while(Tail!=Head)
{
u=Q[Head++];
vis[u]=false;
for(i=head[u];i!=-1;i=E[i].next)
{
v=E[i].v;
w=(double)E[i].w-mid;
if(dis[v]<dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
vis[v]=true;
num[v]++;
if(num[v]>=vn)//有正环
return true;
Q[Tail++]=v;
}
}
}
}
return false;
}
bool Judge(double mid)
{
int i,u,v;
return SPFA(scr,mid);
}
void Solve()
{
double l,r,mid;
l=(double)(Min);
r=(double)(Max);
if(!Judge(0))
{printf("No solution.\n");return;}
while(fabs(l-r)>eps)
{
mid=(l+r)/2.0;
if(Judge(mid))
l=mid;
else
r=mid;
}
printf("%.2lf\n",l);
}
int main()
{
while(scanf("%d",&N),N)
{
Init();
Solve();
}
return 0;
}