#二分,负环#JZOJ 3852 单词接龙
题目
只要一个单词的最后两个字母和另一个单词的前两个字母相同,那么这两个单词就可以有序的连接起来。给出\(n\)个单词组成单词环,求所有环的环中单词平均长度最大值。
分析
二分答案,判断是否存在正环(代码里边权取反求负环),如果存在那么答案不止当前答案
代码
#include <cstdio>
#include <cstring>
#include <queue>
#define rr register
using namespace std;
struct node{int x,y,w,next;}e[100011];
int n,len,k=1,ls[701],cnt,t[701],p[701];
char s[1011]; double dis[701],l,r; bool v[701];
inline bool check(double now){
rr queue<int>q;
for (rr int i=1;i<=cnt;++i)
q.push(i),v[i]=1,t[i]=dis[i]=0;
while (q.size()){
rr int x=q.front(); q.pop();
for (rr int i=ls[x];i;i=e[i].next)
if (dis[e[i].y]>dis[x]+now-e[i].w){
dis[e[i].y]=dis[x]+now-e[i].w;
if (++t[e[i].y]>cnt&&now<e[i].w) return 0;
if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
}
v[x]=0;
}
return 1;
}
inline void add(int x,int y,int w){e[++k]=(node){x,y,w,ls[x]},ls[x]=k;}
signed main(){
scanf("%d",&n),l=1e12;
for (rr int i=1;i<=n;++i){
scanf("%s",s),len=strlen(s),l=l<len?l:len,r=r>len?r:len;
rr int s1=(s[0]^96)*26+(s[1]^96)-26;
rr int s2=(s[len-2]^96)*26+(s[len-1]^96)-26;
if (!p[s1]) p[s1]=++cnt; if (!p[s2]) p[s2]=++cnt;
add(p[s1],p[s2],len);
}
if (check(l)) return !printf("No solution.");
while (1e-2+l<r){
rr double mid=(l+r)/2;
if (check(mid)) r=mid;
else l=mid;
}
return !printf("%lf",l);
}