我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。
如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。
我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。
如下例:
ababc
bckjaca
caahoynaab
第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=22(重复部分算两次),总共使用了 3 个串,所以平均长度是 22/3≈7.33。
输入格式
本题有多组数据。
每组数据的第一行,一个整数 n,表示字符串数量;
接下来 n 行,每行一个长度小于等于 1000 的字符串。
读入以 n=0 结束。
输出格式
若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。
只要答案与标准答案的差不超过 0.01,就视为答案正确。
数据范围
1≤n≤105
输入样例:
3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein
0
输出样例:
21.66
思路:
/*
首先就是如何建图?
假如把字符串作为点,那么连边的时候需要判断n * (n - 1)条边来连边,显然会爆掉
因此必须考虑其它方法
假如我们考虑把字符串的左右端点作为点,那么最多有26*26 = 676个点,边自然最多就是1e5
这个数量级还是可以做的,因此我们按照这个方式建图
然后如何解决->环串的平均长度最大?
按照上述方式建图,我们的边权自然就是字符串的长度
那么就是要使得 (环上边权之和)/ (边的数目) 这个值最大
那这就是一个01分数规划问题了,把式子稍加变形转化成二分判断正(负)环问题
超时?
SPFA判断负环超时的时候,直接把队列换成栈(只针对找正(负)环问题), 找指定环的效率会有所提高
建议使用scanf,效率更高!
*/
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 700, M = 1e5 + 10;
int n, m;
int h[N], ne[M], e[M], w[M], idx;
double dist[N];
int stk[N];
int cnt[N], st[N];
char str[1010];
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 tt = 0; // 手动模拟栈
for (int i = 0; i < 676; i++)
{
stk[++tt] = i;
st[i] = true;
}
while (tt != 0)
{
int t = stk[tt--];
st[t] = false;
for (int i = h[t]; i != -1; 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 (cnt[j] >= N) // 是 N
return true;
if (!st[j])
{
st[j] = 1;
stk[++tt] = j;
}
}
}
}
return false;
}
int main()
{
while (scanf("%d", &n), n)
{
memset(h, -1, sizeof(h));
idx = 0;
for (int i = 1; i <= n; i++)
{
scanf("%s", str);
int len = strlen(str);
if (len >= 2)
{
int left = (str[0] - 'a') * 26 + str[1] - 'a';//哈希存点
int right = (str[len - 2] - 'a') * 26 + str[len - 1] - 'a';
add(left, right, len);
}
}
double l = 0, r = 1000;
while (r - l > 1e-4) // 01分数规划就二分
{
double mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
if (l < 1e-4) // 在最左边则无解
puts("No solution");
else
printf("%lf\n", r);
}
return 0;
}