jzoj3852-单词接龙【0/1分数规划,负环】

正题

题目链接:https://jzoj.net/senior/#main/show/3852


题目大意

n n n个单词串,头尾有两个相同单词就可以连在一起,求一个最长的环使得平均单词长度最长。


解题思路

其实总共 26 ∗ 26 26*26 2626个点,然后求一个回路使得平均边长最长

就是0/1分数规划问题

二分一个答案 m i d mid mid,将边权改为 m i d − w mid-w midw,然后跑负环判断即可。


c o d e code code

#include
#include
#include
#include
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.");
}

你可能感兴趣的:(图论,二分法)