整理的算法模板: ACM算法模板总结(分类详细版)
我们有 nn 个字符串,每个字符串都是由 a∼za∼z 的小写英文字母组成的。
如果字符串 AA 的结尾两个字符刚好与字符串 BB 的开头两个字符相匹配,那么我们称 AA 与 BB 能够相连(注意:AA 能与 BB 相连不代表 BB 能与 AA 相连)。
我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。
如下例:
ababc
bckjaca
caahoynaab
第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=225+7+10=22(重复部分算两次),总共使用了 33 个串,所以平均长度是 223≈7.33223≈7.33。
输入格式
本题有多组数据。
每组数据的第一行,一个整数 nn,表示字符串数量;
接下来 nn 行,每行一个长度小于等于 10001000 的字符串。
读入以 n=0n=0 结束。
输出格式
若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。
只要答案与标准答案的差不超过 0.010.01,就视为答案正确。
数据范围
1≤n≤1051≤n≤105
输入样例:
3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein
0
输出样例:
21.66
思路:
直接把每个字符串的编号进行建图数据量太庞大(如果是1e5个aaaa的话那么要建1e10条边,直接gg);
所以可以将字符串的前两个字符按哈希看成一个点,同理后两个字符也一样;然后一个字符串就可以看成前后两个点连成的一条边了,这样一共也就有26*26=676个点,然后最多676*676条边;
处理完所有字符串之后,二分mid(mid为要 求的平均值);用spfa算法看图中有没有正环,;如果有正环,说明此时的mid小了,反之亦然;
spfa算法中要对边进行处理:
判正环求最长路,判负环求最短路;
注意当mid=0的时候都没有正环的话,那么一定是没有正环;
(然后我吐了.....)玄学优化,当访问点的总次数达到2~5倍的总点数时,就可直接return 说明有环;
#include <bits/stdc++.h>
using namespace std;
const int N=700,M=1e5+7;
int e[M],h[M],ne[M],w[M],idx,n,cnt[N];
double dis[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa(double mid)
{
memset(dis,-0x3f,sizeof dis);
memset(st,false,sizeof st);
memset(cnt,0,sizeof cnt);
queue<int> q;
for(int i=0;i<676;i++) q.push(i),st[i]=true;
int count=0;
while(q.size())
{
int t=q.front();
st[t]=false;
q.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]<dis[t]+w[i]-mid)
{
dis[j]=dis[t]+w[i]-mid;
cnt[j]=cnt[t]+1;
if(++count>4*N) return true;
if(cnt[j]>=N) return true;
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
while(cin >>n&&n)
{
idx=0;
memset(h,-1,sizeof h);
for(int i=0;i<n;i++)
{
string s;
cin >>s;
int len=s.size();
if(len>=2)
{
int l=(s[0]-'a')*26+s[1]-'a',r=(s[len-2]-'a')*26+s[len-1]-'a';
add(l,r,len);
}
}
if(!spfa(0)) puts("No solution");
else
{
double l=0,r=1000;
while(r-l>1e-4)
{
double mid=(l+r)/2;
if(spfa(mid)) l=mid;
else r=mid;
}
printf("%lf\n",l);
}
}
}