AcWing 1165. 单词环

题目

我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。

如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。

我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。

如下例:
在这里插入图片描述
第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=22(重复部分算两次),总共使用了 3 个串,所以平均长度是 22/3≈7.33。

输入格式

本题有多组数据。

每组数据的第一行,一个整数 n,表示字符串数量;

接下来 n 行,每行一个长度小于等于 1000 的字符串。

读入以 n=0 结束。

输出格式

若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。

只要答案与标准答案的差不超过 0.01,就视为答案正确。

数据范围

1≤n≤10^5

思路

我们把输入的每个字符串当做一条边,点及只有26*26个,然后再二分答案跑spfa即可。spfa中有个玄学优化,访问点的次数为总点数的几倍的时候,即可认为它含环。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;

const int N=700, M = 100010;

int n;
int h[N],e[M],ne[M],w[M],idx;
double dist[N];
int q[N],cnt[N];
bool st[N];

void add(int a,int b,int c)
{
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool check(double mid)
{
	memset(st,0,sizeof st);
	memset(cnt,0,sizeof cnt);
	memset(dist,0,sizeof dist);
	
	int hh=0,tt=0;
	for(int i=0;i<676;i++)
	{
		q[tt++]=i;
		st[i]=true;
	}
	int count = 0;
	while(hh!=tt)
	{
		int t=q[hh++];
		if(hh==N) hh=0;
		st[t]=false;
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(dist[j]<dist[t]+w[i]-mid)
			{
				dist[j]=dist[t]+w[i]-mid;
				cnt[j]=cnt[t]+1;
				
				if(++count > 10000) return true;
				
				if(cnt[j]>=N) return true;
				
				if(!st[j])
				{
					q[tt++]=j;
					if(tt==N) tt=0;
					st[j]=true;
				}
			}
		}
	}
	return false;
}

int main()
{
	//freopen("test.in","r",stdin);//设置 cin scanf 这些输入流都从 test.in中读取
    //freopen("test.out","w",stdout);//设置 cout printf 这些输出流都输出到 test.out里面去
	//ios::sync_with_stdio(false);
	//cin.tie(0),cout.tie(0);
	while(cin>>n)
	{
		if(n==0) break;
		memset(h,-1,sizeof h);
		idx=0;
		for(int i=0;i<n;i++)
		{
			string s;
			cin>>s;
			if(s.length()<2) continue;
			int first,second,w;
			int len=s.length();
			first=(s[0]-'a')*26 + (s[1]-'a');
			second=(s[len-2]-'a')*26 + (s[len-1]-'a');
			add(first,second,len);
		}
		if(!check(0)) cout<<"No solution"<<endl;
		else
		{
			double l=0,r=1010;
			while(r-l>1e-4)
			{
				double mid=(l+r)/2;
				if(check(mid)) l=mid;
				else r=mid;
			}
			//cout<<l<<" "<<r<<endl;
			printf("%lf\n",r);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44828887/article/details/107271849