这道题主要是建图困难,如果我们直接建图,那么有1e5个点,1e10条边,肯定超时。
我们取每个单词的前两个字母,以及后两个字母为节点,单词的长度为边,那么我们可以将规模缩小到,26*26个点,1e5条边这个复杂度小很多。
0/1整数规划
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define x first
#define y second
typedef pair<int,int> PII;
const int N=676,M=1e5+10;
int n,m,k,ans,ecnt;
int head[N],vis[N],cnt[N];
double d[N];
struct edge {
int u,v,w,next;
} E[M];
void add(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;
}
bool check(double mid) {
memset(vis,0,sizeof vis);
memset(d,0,sizeof d);
memset(cnt,0,sizeof cnt);
queue<int>q;
for(int i=0; i<676; i++) {
q.push(i);
vis[i]=1;
}
int count=0;
while(!q.empty()) {
int u=q.front();
vis[u]=0;
q.pop();
for(int i=head[u]; i; i=E[i].next) {
int v=E[i].v;
if(d[v]<d[u]+E[i].w-mid) {
d[v]=d[u]+E[i].w-mid;//0/1整数规划
cnt[v]=cnt[u]+1;//最短路边数
if(++count>1e4)return true;//SPFA 如果更新次数大于两倍,直接结束,算成功
if(cnt[v]>=N)return true;//存在正环
if(!vis[v]) {
q.push(v);
vis[v]=1;
}
}
}
}
return false;
}
int main() {
while(cin>>n) {
if(n==0)break;
memset(head,0,sizeof head);
ecnt=0;
string str;
for(int i=0; i<n; i++) {
cin>>str;
int len=str.size();
if(len>=2) {
int left=(str[0]-'a')*26+str[1]-'a',
right=(str[len-2]-'a')*26+str[len-1]-'a';
add(left,right,len);
}
}
if(!check(0)) {
puts("No solution");
} else {
double l=0,r=1000;
while(r-l>1e-4) {
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%lf\n",r);
}
}
return 0;
}