Play on Words,UVA 10129——求欧拉回路/欧拉通道

Play on Words,UVA 10129——求欧拉回路/欧拉通路

Play on words


紫书上的一道题:

输入n(n<=100000)个单词,是否可以把所有这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同。每个单词最多包含1000个小写字母。输入中可以有重复单词。



分析:

把字母看成结点,单词看成有向边,则问题有解,当且仅当图中存在欧拉通路/欧拉回路

欧拉通路/回路:通过图中所有边一次且仅一次行遍所有顶点的通路/回路

有向图存在欧拉回路的判断条件:当且仅当有向图是强连通的,且没有奇度顶点,所有顶点的入度等于出度

有向图存在欧拉通路的判断条件:当且仅当有向图是单向连通的,且恰有两个奇度顶点,一个顶点的入度比出度大一,另一个顶点的出度比入度大一,其余顶点的入度等于出度


判断连通性,dfs,bfs,Union-Find Set


注:百度上用dfs写的代码都过不了这个样例:

1

3

dc

cb

ba

因为除非是欧拉回路才可以任意的起点,如果存在奇度顶点,就必须以两个奇度顶点为起点和终点


如下附上百度到的代码,最后面附上用并查集写的ac的代码

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
int Map[26][26];
int in[26],out[26];
bool vis[26];
int T,N;
void dfs(int k)
{
    vis[k]=true;
    for(int i=0;i<26;i++)
    {
        if(Map[k][i]&&!vis[i])
            dfs(i);
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        char C[1100];
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(Map,0,sizeof(Map));
        for(int i=0;i<N;i++)
        {
            scanf("%s",C);
            int len=strlen(C);
            ++Map[C[0]-'a'][C[len-1]-'a'];
            ++out[C[0]-'a'];
            ++in[C[len-1]-'a'];
        }
        int a=0,b=0;
        bool flag=true;
        for(int i=0;i<26;i++)
        {
            if(in[i]!=out[i])
            {
                if(in[i]==out[i]+1) a++;
                else if(in[i]+1==out[i]) b++;
                else {flag=false;break;}
            }
        }
        if(a&&b&&a+b>2)  flag=false;
        if(flag)
        {
            memset(vis,false,sizeof(vis));
            for(int i=0;i<26;i++)
            if(out[i]) {dfs(i);break;}
            bool flag1=true;
            for(int i=0;i<26;i++)
            {
                if(out[i]&&!vis[i]) {flag1=false;break;}
                if(in[i]&&!vis[i]) {flag1=false;break;}
            }
            if(flag1)
            {
                cout<<"Ordering is possible.\n";
            }
            else {cout<<"The door cannot be opened.\n";}
        }
        else {
            cout<<"The door cannot be opened.\n";
        }

    }
    return 0;
}




并查集:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=a;i<b;++i)
#define mes(s,c) memset(s,c,sizeof(s))
using namespace std;
int in[26],ou[26];
char str[1010];
int fa[26];
int n;
void Init()
{
    mes(in,0),mes(ou,0);
    FOR(i,0,26){
        fa[i]=i;
    }
}
int Find(int x)
{
    return fa[x]==x ? x:fa[x]=Find(fa[x]);
}
bool check_degree()
{
    int num1=0,num2=0;
    for(int i=0;i<26;++i){
        if(in[i]!=ou[i]){
            if(in[i]==ou[i]+1) num1++;
            else if(in[i]+1==ou[i]) num2++;
            else return false;
        }
    }
    if(num1&&num2&&num1+num2>2) return false;
    return true;
}
bool check_connected()
{
    int cnt=0;
    FOR(i,0,26){
        if( (in[i]||ou[i])&&fa[i]==i){
            ++cnt;
        }
    }
//    cout<<"cnt="<<cnt<<endl;
    if(cnt!=1) return false;
    return true;
}
void solve()
{
    if(check_degree()){
        if(check_connected())
            printf("Ordering is possible.\n");
        else
            printf("The door cannot be opened.\n");
    }
    else{
        printf("The door cannot be opened.\n");
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--){
        Init();
        scanf("%d",&n);
        REP(i,n){
            scanf("%s",str);
            int l=strlen(str);
            int x=str[0]-'a';
            int y=str[l-1]-'a';
            in[y]++;
            ou[x]++;
            fa[Find(x)]=Find(y);
        }
        solve();
    }
    return 0;
}