正题
题目链接:https://jzoj.net/senior/#main/show/3852
题目大意
个单词串,头尾有两个相同单词就可以连在一起,求一个最长的环使得平均单词长度最长。
解题思路
其实总共 个点,然后求一个回路使得平均边长最长
就是0/1分数规划问题
二分一个答案 ,将边权改为 ,然后跑负环判断即可。
#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.");
}