#二分,负环#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);
}
posted @ 2020-02-15 13:42  lemondinosaur  阅读(110)  评论(0编辑  收藏  举报