【JZOJ 3852】单词接龙

本文介绍了一种通过二分查找及SPFA算法解决单词接龙游戏中寻找最长平均长度单词环的问题。采用图论方法,将单词视作边,首尾字母组合视为节点,通过构建图并遍历来确定是否存在满足条件的单词环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

Solution

看到平均和最值,差不多就是二分答案,
把单词看成边,两个字母看成点,先连上边,(注意,是单向的)
二分一个答案,把每一条边都减掉二分出的答案,全局跑一遍SPFA,看看有没有正环,有就增加,没有就减小;

显然,如果有正环,SPFA就会不停的跑下去,判断有没有正环,就设一个阈值,如果SPFA跑的次数超过了,就说明有正环,

复杂度:O(log(109)k)(k为设的阈值)

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef double db;
const int N=30500;
const db M=1e-6;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,TI;
int B[N*4][2],A[N],B0=1;
db ans;
int z[N],d[10*N];
db JA;
db d1[N];
int b1[2700][2700];
void link(int q,int w){B[++B0][0]=A[q];A[q]=B0,B[B0][1]=w;}
bool SPFA()
{
    int s=1,t=m,q;
    int CT=0;
    TI+=2;
    fo(i,1,m)d1[d[i]]=0,z[d[i]]=TI-1;
    while(s<=t)
    {
        CT++;
        if(CT>4*n)return 1;
        q=d[s++];
        efo(i,q)if(d1[q]+b1[q][B[i][1]]-JA+M>d1[B[i][1]])
        {
            d1[B[i][1]]=d1[q]+b1[q][B[i][1]]-JA;
            if(z[B[i][1]]<TI-1)z[B[i][1]]=TI-1,d[++t]=B[i][1];
            if(z[B[i][1]]==TI-1)z[B[i][1]]=TI,d[++t]=B[i][1];
        }
        z[q]=TI-2;
    }
    return 0;
}
int main()
{
    int q,w,e;
    scanf("%d",&n);
    fo(i,1,n)
    {
        char ch=' ',ch1,ch2;
        while(ch<'a'||ch>'z')ch=getchar();
        ch1=getchar();
        q=(ch-96)*100+ch1-96;ch2=ch;
        e=1;
        for(;ch>='a'&&ch<='z';ch=getchar(),e++)ch2=ch1,ch1=ch;
        w=(ch2-96)*100+ch1-96;
        if(!b1[q][w])b1[q][w]=e,link(q,w);
        b1[q][w]=max(b1[q][w],e);
    }
    fo(i,1,26)fo(j,1,26)if(A[i*100+j])d[++m]=i*100+j;
    db l=0,r=1e9;
    while(r-l>2e-3)
    {
        db t=(l+r)/2;JA=t;
        if(SPFA())l=t;
            else r=t;
    }
    printf("%.4lf\n",r);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值