正题
题目链接:https://jzoj.net/senior/#main/show/3852
题目大意
nnn个单词串,头尾有两个相同单词就可以连在一起,求一个最长的环使得平均单词长度最长。
解题思路
其实总共26∗2626*2626∗26个点,然后求一个回路使得平均边长最长
就是0/1分数规划问题
二分一个答案midmidmid,将边权改为mid−wmid-wmid−w,然后跑负环判断即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=800;
const double eps=1e-5;
struct node{int to,next,c;double w;
}a[100010];
queue<int> q;
int n,ls[N],cnt[N],tot;
char s[1100];
bool v[N];
double f[N];
void addl(int x,int y,int c){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].c=c;
}
bool SPFA(){for(int i=1;i<N;i++)f[i]=0,v[i]=cnt[i]=1,q.push(i);memset(cnt,0,sizeof(cnt));while(!q.empty()){int x=q.front();q.pop();v[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(f[x]+a[i].w<f[y]){f[y]=f[x]+a[i].w;cnt[y]=cnt[x]+1;if(cnt[y]>=N&&a[i].w<0)return 1;if(!v[y]){v[y]=1;q.push(y);}}}}return 0;
}
bool check(double x){for(int i=1;i<=tot;i++)a[i].w=x-a[i].c;if(SPFA()) return 1;return 0;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",s+1);int l=strlen(s+1);int x=(s[1]-'a')*26+s[2]-'a'+1,y=(s[l-1]-'a')*26+s[l]+1-'a';addl(x,y,l);}double l=2,r=1000;while(r-l>eps){double mid=(l+r)/2;if(check(mid)) l=mid;else r=mid;}if(check(l))printf("%lf",l);else printf("No solution.");
}