LibreOJ10082. 「一本通 3.3 例 1」Word Rings【二分+SPFA】

10082. 「一本通 3.3 例 1」Word Rings

【题目描述】

传送门

【题解】

将一个字符串看成一条边,字符两端的字符看成节点,长度看成权值。二分枚举答案,最后SPFA刷正环,因为只要有一个正环存在就可以了。

代码如下

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN=26*26,MAXE=1e5+5;
int n;bool ST,vis[MAXN+5];char ch[1005];
double Ans,mid,dst[MAXN+5];
struct Edge{
	int tot,lnk[MAXN+5],son[MAXE],nxt[MAXE];double W[MAXE];
	void clear(){memset(lnk,0,sizeof(lnk)),tot=0;}
	void Add(int x,int y,int w){son[++tot]=y;W[tot]=w;nxt[tot]=lnk[x];lnk[x]=tot;}
}E;
void SPFA(int x){
	if(ST) return;vis[x]=1;
	for(int j=E.lnk[x];j;j=E.nxt[j])
	if(dst[E.son[j]]<dst[x]+E.W[j]-mid){
		if(vis[E.son[j]]){ST=1;return;}
		dst[E.son[j]]=dst[x]+E.W[j]-mid,SPFA(E.son[j]);
	}
	vis[x]=0;
}
bool check(){
	memset(dst,0,sizeof(dst)),memset(vis,0,sizeof(vis));ST=0;
	for(int i=0;i<MAXN;i++){SPFA(i);if(ST) return 1;}
	return 0;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("prob.in","r",stdin);
	freopen("prob.out","w",stdout);
	#endif
	while(scanf("%d",&n),n){
		E.clear();
		for(int i=1,Len;i<=n;i++) scanf("%s",ch),Len=strlen(ch),E.Add((ch[0]-'a')*26+(ch[1]-'a'),(ch[Len-2]-'a')*26+(ch[Len-1]-'a'),Len);
		double L=0,R=1001;
		for(mid=(R+L)/2;R-L>1e-4;mid=(R+L)/2)
		if(check()) Ans=L=mid;else R=mid;
		if(Ans==0) printf("No solution\n");else printf("%.3lf\n",Ans);
	}
	return 0;
}
posted @ 2018-09-19 21:20  XSamsara  阅读(305)  评论(0编辑  收藏  举报