JZOJ 3852. 【NOIP2014八校联考第2场第2试9.28】单词接龙(words)

10 篇文章 0 订阅
3 篇文章 0 订阅

Description

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

Input

第一行一个整数N,表示单词数量。
接下来N行,每行一个字符串,仅包含小写字母。

Output

若能组成单词环,输出环中单词的最大平均长度,结果保留2位小数;否则输出”No solution.”(不包括双引号)。精度误差在0.01都算正确。

Sample Input

3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein

Sample Output

21.67

Data Constraint

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

Solution

  • 把问题转换一下,可以这样处理:

  • 把一个单词的前两个单词和后两个单词视为点,单词长度视为边权

  • 那么点数最多为 2626=676 个点,这样在其中找环即可

  • 看到题目问的是最大值,那么可以二分答案 len ,每一条边权值减去 len

  • 于是问题变成判定是否存在正权环!有则 len 可以更大,没有则更小。

  • 对于判定环,可以使用 SPFA,BFS和DFS都可以,每个点跑一次最长路即可

  • 详情请见我的博客:http://blog.csdn.net/liyizhixl/article/details/54565085

  • (SPFA算法及判负环方法)

  • 注意走过的点不用再做SPFA,时间复杂度 O(NlogN)

Code

#include<cstdio>
#include<cstring>
using namespace std;
const int N=3000001,M=26*26+1;
const double E=1e-3;
int n,tot;
int node[M],que[N];
double dis[M],w[N];
int first[M],next[N],en[N];
bool bz[M],vis[M];
char s[1002];
inline int get(int x,int y)
{
    return 26*(x-'a')+y-'a'+1;
}
inline void insert(int x,int y,int z)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline bool spfa(int st,double num)
{
    for(int i=1;i<=node[i];i++) dis[node[i]]=0;
    int l=0,r=1;
    vis[que[1]=st]=true;
    while(l<r)
    {
        int now=que[++l];
        bz[now]=false;
        for(int i=first[now];i;i=next[i])
            if(dis[now]+w[i]-num>dis[en[i]])
            {
                dis[en[i]]=dis[now]+w[i]-num;
                if(!bz[en[i]])
                {
                    bz[que[++r]=en[i]]=vis[en[i]]=true;
                    if(r>n) return true;
                }
            }
    }
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        int len=strlen(s+1);
        if(len==1) continue;
        int x=get(s[1],s[2]),y=get(s[len-1],s[len]);
        if(x!=y) insert(x,y,len);
        if(!vis[x]) vis[node[++node[0]]=x]=true;
        if(!vis[y]) vis[node[++node[0]]=y]=true;
    }
    double l=0,r=1000;
    while(l+E<r)
    {
        double mid=(l+r)/2;
        for(int i=1;i<=node[0];i++) vis[node[i]]=false;
        bool pd=false;
        for(int i=1;i<=node[0];i++)
            if(!vis[node[i]] && spfa(node[i],mid))
            {
                pd=true;
                break;
            }
        if(pd) l=mid; else r=mid;
    }
    if(!l) printf("No solution."); else printf("%.2lf",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值