【JZOJ3852】【spfa判负环】【NOIP2014八校联考第2场第2试9.28】单词接龙(words)

17 篇文章 0 订阅
12 篇文章 0 订阅

Description

Bsny从字典挑出N个单词,并设计了接龙游戏,只要一个单词的最后两个字母和另一个单词的前两个字母相同,那么这两个单词就可以有序的连接起来。
Bsny想要知道在所给的所有单词中能否按照上述方式接龙组成一个单词环(可能是多个),若能,求所有环的环中单词平均长度最大值。

Data Constraint

20%的数据:n≤20;
70%的数据:n≤1000;
100%的数据:n≤100000,每个单词长度不超过1000。输入数据比较大,C/C++的同学用scanf输入。

Solution

相信大家都很容易想到将前2个和后2个连边。(反正我是在想了1个小时后才想到的……)然后现在问题来了,怎样选边让它平均值最大?我们想到了二分。显然我们二分出一个值mid后,将所右边都减去一个mid,跑一个spfa最长路,若跑出一个环则为合法,往上走,否则往下走。

解释一下

因为我们跑的是最长路,所以当一个环的所有边的和为负数时,他是不可能让到达每个点的权值变得更大的,所以就不可能构成一个环重复跑,也就是说spfa跑最长路跑出的环一定为一个合法的正数环。

那么spfa怎样判环呢

跑spfa的时候,假设一个点在队列中出现的次数的顶点个数,那么图中一定存在一个环。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e6+5;
int first[maxn],last[maxn],next[maxn],v[4000000],bz[maxn],bz2[maxn],b[maxn];
int n,i,t,j,k,x,y,num,p,c[1000][1000];
double l,r,mid,value[maxn],d[maxn],xx;
char s[maxn];
bool bz1;
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
bool spfa1(){
    int i=0,j=1,t,k,l,x,y;
    for (k=1;k<=27*27;k++)
        d[k]=-100000000000;
    memset(bz2,0,sizeof(bz2));
    d[v[1]]=0;bz[v[1]]=1;bz2[v[1]]=1;
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            if (d[x]+value[t]<=d[last[t]]) continue;
            d[last[t]]=d[x]+value[t];
            if (!bz2[last[t]]){
                v[++j]=last[t],bz2[v[j]]++,bz[v[j]]++;
                if (bz[v[j]]>p)return 1;
            }
        }
        bz2[x]=0;
    }
    return 0;
}
bool spfa(){
    int i=0,j=1,t,k,l,x,y;memset(bz,0,sizeof(bz));
    for (k=1;k<=27*27;k++)
        if (b[k] && !bz[k]){
            v[1]=k;t=spfa1();
            if (t) return t;
        }
    return 0;
}
int main(){
    //freopen("data.in","r",stdin);
    scanf("%d\n",&n);
    for (i=1;i<=n;i++){
        scanf("%s\n",s+1);
        x=(s[1]-96)*27+s[2]-96;t=strlen(s+1);
        y=(s[t-1]-96)*27+s[t]-96;
        c[x][y]=max(c[x][y],t);
        if (!b[x]) p++;if (!b[y]) p++;
        b[x]=b[y]=1;
    }
    for (i=1;i<=27*27;i++)
        for (j=1;j<=27*27;j++)
            if (c[i][j]) lian(i,j,c[i][j]);
    l=1;
    r=1000;
    while (r-l>0.001){
        mid=(l+r)/2;xx=mid;
        for (i=1;i<=n;i++)
            value[i]-=mid;
        if (spfa()) l=mid;
        else r=mid;
        for (i=1;i<=n;i++)
            value[i]+=xx;
    }
    if (l>1) printf("%.2lf\n",l);
    else printf("No solution.\n");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值