题目
n(n<=1e5)个纯小写字母组成的单词,第i个单词长为|si|(|si|<=1e3)
你可以取一个或一些单词,使得第一个单词的末两位与第二个单词的首两位相同,
第二与第三,……,最后一个的末两位与第一个的首两位相同
每个单词只能用一次,假设你取了k个单词,单词总长是len,输出最大化的len/k的值
题解
单纯想总结一下,最大平均环问题
将每个单词最开始的两个字母和最后的两个字母分别抽象成两个点,
你需要找到一个环,使得环的平均值最大,
则二分最大的x,使得每条边都减去这个值x后,仍然存在正环,则x就是答案
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define fi first
#define se second
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const int N=26*26,M=1e3+10;
int m,u,v;
char s[M];
int cnt[N];
double dis[N];
vector<P>E[N];
bool vis[N];
void add(int u,int v,int w){
E[u].push_back(P(v,w));
}
bool spfa(double x)//最长路 {
memset(vis,0,sizeof vis);
memset(cnt,0,sizeof cnt);
memset(dis,0,sizeof dis);
queue<int>q;
for(int i=0;i<N;++i){//类似多源bfs 建一个超级源点加进去
vis[i]=cnt[i]=1;
q.push(i);
}
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<E[u].size();++i){
int v=E[u][i].fi,w=E[u][i].se;
if(dis[v]<dis[u]+w-x){
dis[v]=dis[u]+w-x;
cnt[v]=cnt[u]+1;
if(cnt[v]>N)return 1;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
return 0;
}
int f(char x,char y){
int a=x-'a',b=y-'a';
return a*26+b;
}
int main(){
while(~scanf("%d",&m)&&m){
for(int i=0;i<N;++i){
E[i].clear();
}
double l=0,r=0,mid;
for(int i=1;i<=m;++i){
scanf("%s",s);
int len=strlen(s);
if(len<2)continue;
r=max(r,(double)len);
u=f(s[0],s[1]);
v=f(s[len-2],s[len-1]);
//printf("u:%d v:%d len:%d\n",u,v,len);
add(u,v,len);
}
for(int i=0;i<30;++i){
mid=(l+r)/2;
if(spfa(mid))l=mid;
else r=mid;
}
if(spfa(l))printf("%.2lf\n",l);
else puts("No solution.");
}
return 0;
}