Fork me on GitHub

UESTC 917 方老师的分身IV --求欧拉路径

判断欧拉路径是否存在及求出字典序最小的欧拉路径问题(如果存在)。

将字符串的第一个字母和最后一个字母间连边,将字母看成点,最多可能有26个点(a-z),如果有欧拉路径,还要判断是否有欧拉回路,如果有,则需要找一个字典序最小的点开始生成这条链,否则以起点开始生成链,起点即为出度比入度大1的点。

欧拉路径是否存在的判定:

1.全部点在一个联通块                               ----用并查集判联通块的数量
2.所有点出度入度相等                               ----in[],out[]记录出度与入度
3.或者有一个出度比入度小1,另一个出度比入度大1(源点与汇点)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#define Mod 1000000007
using namespace std;
#define N 2007

string ss[N];

struct node
{
    int u,v,next;
    string w;
}G[N];

int first[28],fa[28],in[28],out[28],vis[28];
int tot,k,start;
int evis[N];
string ans[N];

void init()
{
    int i;
    for(i=0;i<28;i++)
        fa[i] = i;
    tot = 1;
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    for(i=0;i<=2000;i++)
    {
        G[i].u = G[i].v = G[i].next = 0;
        G[i].w = "";
    }
    memset(first,-1,sizeof(first));
    memset(vis,0,sizeof(vis));
    memset(evis,0,sizeof(evis));
    for(int j=0;j<=2000;j++)
        ans[j] = "";
}

int findset(int x)
{
    if(x != fa[x])
        fa[x] = findset(fa[x]);
    return fa[x];
}

int cmp(string ka,string kb)
{
    return ka>kb;
}

void addedge(int u,int v,string w)
{
    G[tot].u = u;
    G[tot].v = v;
    G[tot].w = w;
    G[tot].next = first[u];
    first[u] = tot++;
    out[u]++;
    in[v]++;
    vis[u] = vis[v] = 1;
    int fx = findset(u);
    int fy = findset(v);
    if(fx != fy)
        fa[fx] = fy;
}

int Euler_Path()
{
    int block = 0,i;
    int O = 0;
    int I = 0;
    start = -1;
    for(i=0;i<26;i++)
    {
        if(vis[i])   //涉及到的字母
        {
            int fk = findset(i);
            if(fk == i)
                block++;
            if(block > 1)
                return false;
            if(in[i] == out[i]+1)  //入大出,奇度数点
                I++;
            else if(in[i]+1 == out[i]) //出大入,奇度数点
                O++,start = i;
            else if(in[i] != out[i])
                return false;
        }
    }
    if(block != 1)
        return false;
    if((O == I && O == 1) || (O == I && O == 0)) //没有奇度数点或者只有源点和汇点是奇度数点
    {
        if(start == -1)   //没有找到起点,是欧拉回路
        {
            for(i=0;i<26;i++)
            {
                if(vis[i] && out[i] > 0)  //找字典序最小的字母做起点
                {
                    start = i;
                    return true;
                }
            }
        }
        //如果已找到起点,则不能是欧拉回路
        if(O == I && O == 1)
            return true;
        return false;
    }
    return false;
}

void dfs(int v,int flag)
{
    for(int e=first[v];e!=-1;e=G[e].next)
    {
        if(!evis[e])  //边没被访问
        {
            evis[e] = 1;
            dfs(G[e].v,e);
        }
    }
    if(flag != -1)
        ans[k++] = G[flag].w;
}

int main()
{
    int t,i,j,n;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            cin>>ss[i];
        sort(ss+1,ss+n+1,cmp);
        //qsort(ss+1,n,28,cmp);
        for(i=1;i<=n;i++)
        {
            int u = ss[i][0]-'a';
            int v = ss[i][ss[i].length()-1]-'a';
            addedge(u,v,ss[i]);
        }
        if(Euler_Path())
        {
            k = 0;
            dfs(start,-1);
            cout<<ans[k-1];
            for(i=k-2;i>=0;i--)
                cout<<"."<<ans[i];
            printf("\n");
        }
        else
            puts("***");
    }
    return 0;
}
View Code

 

 

posted @ 2014-06-15 10:37  whatbeg  阅读(372)  评论(0编辑  收藏  举报