欧拉回路 && 欧拉路径

欧拉路径()定义 : 如果有一条路径使得能够走完所有的边且每一条边经过有且只有一次,这样的路径叫做欧拉路径

欧拉回路定义 : 如果有从起点出发最后回到起点的一条路径使得能够走完所有的边且每条边经过有且只有一次,称其为欧拉回路

根据定义,欧拉回路是欧拉路径的一个子集,存在欧拉回路定存欧拉路径,反之则不一定成立

半欧拉图 : 有欧拉路径而没有欧拉回路的图

欧拉图 : 包含至少一个欧拉回路的图

 

如何判断一幅图是否有欧拉路径/回路? PS : 以下的欧拉路径条件都是针对半欧拉图来说

① 有向图情况

欧拉路径 ==> 有且只有一个点的出度 - 入度 == 1、有且只有一个点的入度 - 出度 == 1、其余点的出入度相等

欧拉回路 ==> 所有点的出度 == 入度

② 无向图情况

欧拉路径 ==> 有且只有两个点的度为奇数(起点、终点)、其他点的度为偶数

欧拉回路 ==> 所有点的度都为偶数

 

以上只能判定路径的有无、而要找到其中一条欧拉路径/回路,有两种算法

一种是 Fluery 算法、一种是 Hierhoizers 算法

Fluery 算法 : 略......、给个链接

Hierhoizers 算法 : 

此算法是基于 DFS 的路径回复算法,前提条件是给其指定好起点

算法会自动寻找欧拉回路、找不到的情况下会找到欧拉路径

/*
开始DFS递归函数(当前顶点 x):
    寻找与 x 相连的边(x,v):
        删除 (x,v)
        删除 (v,x)///如果是无向图的话
        DFS(v)
    将x插入到路径栈中
*/

struct EDGE{ int v, nxt; bool used; }; ///链式向前星边结构体定义
stack<int> path; ///定义栈记录顶点路径

void DFS(int x)
{
    for(int i=Head[x]; i!=-1; i=Edge[i].nxt){
        int Eiv = Edge[i].v;
        if(!Edge[i].used){
            Edge[i].used = true;
            DFS(x);
        }
    }
    path.push(x);
}

 

例题 POJ 2337 Catenyms

以 26 个字母为顶点、以给出的单词为边,找出一条字典序最小的欧拉路径就是答案

由于这里要字典序最小,基于我们寻找欧拉路径的DFS算法,所以在进行建图的时候

最好将单词进行字典序排序再来插入到邻接表中即可达到这个目的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
struct EDGE{ int v, nxt, id; bool used; }Edge[maxn<<2];
int Head[30], cnt, N;
int IN[30], OUT[30];
int St;
int ans[maxn<<2], len;
string str[maxn];

inline void init()
{
    memset(Head, -1, sizeof(Head));
    memset(IN, 0, sizeof(IN));
    memset(OUT, 0, sizeof(OUT));
    cnt = 0;
    St = 0x3f3f3f3f;
}

inline void AddEdge(int From, int To, int ID)
{
    Edge[cnt].used = false;
    Edge[cnt].id = ID;
    Edge[cnt].v = To;
    Edge[cnt].nxt = Head[From];
    Head[From] = cnt++;
}

void DFS(int v, int id)///由于是要边路径而不是顶点路径,所以要带个边参数
{
    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){
        if(!Edge[i].used){
            Edge[i].used = true;
            int Eiv = Edge[i].v;
            DFS(Eiv, Edge[i].id);
        }
    }
    if(id != -1)
        ans[len++] = id;

//    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){///如果不像代边的编号参数,可以这样写
//        if(!Edge[i].used){                   ///也可以达到记录边路径的作用
//            Edge[i].used = true;
//            DFS(Edge[i].v);
//            ans[len++] = Edge[i].id;
//        }
//    }
}

int main(void)
{
    int nCase;
    scanf("%d", &nCase);
    while(nCase--){

        init();

        scanf("%d", &N);
        for(int i=0; i<N; i++)
            cin>>str[i];
        sort(str, str+N);

        for(int i=N-1; i>=0; i--){///将边从大的到小的插入,因为是链式向前星存储
            int Len = str[i].length();
            int From = str[i][0] - 'a';
            int To = str[i][Len-1] - 'a';
            AddEdge(From, To, i);
            IN[To]++, OUT[From]++;
            St = min(To, min(St, From));///记录一下DFS的起点,对应了有欧拉回路的情况
        }

        int Not_equal, St_num, Des_num;
        Not_equal = St_num = Des_num = 0;

        for(int i=0; i<26; i++){
            if(!IN[i] && !OUT[i]) continue;
            if(IN[i] != OUT[i]) Not_equal++;
            if(OUT[i] - IN[i] == 1){
                St_num++;
                St = i;///此时已经可以判定没有欧拉回路了,改变起点,将出度多的作为起点
            }else if(IN[i] - OUT[i] == 1)
                Des_num++;
        }

        if(Not_equal > 0){///如果有点的出入度不想等
            if(!(Not_equal == 2 && St_num == 1 && Des_num == 1)){///并没有欧拉路径
                puts("***");
                continue;
            }
        }

        len = 0;
        DFS(St, -1);///恢复欧拉路径

        if(len != N){
            puts("***");
            continue;
        }

        for(int i=len-1; i>=0; i--){
            if(ans[i] == -1) continue;
            cout<<str[ans[i]];
            if(i > 0) putchar('.');
            else puts("");
        }
    }
    return 0;
}
/*
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
*/
View Code

 

转载于:https://www.cnblogs.com/LiHior/p/8747203.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值