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

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

你可能感兴趣的:(noip,最短路,环上问题,二分)