欧拉回路、欧拉通路、欧拉图、半欧拉图等有关欧拉图的讲解与代码实现

  有人说,图论的起源,就是源于欧拉图(千万别看成柏拉图)——题记


首先,先要讲一些有必要知道的东西:当然,我在这里也写过,这里再给出一些拓展的内容

欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路

欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路

有向图的基图:忽略有向图所有边的方向,得到的无向图称为该有向图的基图。 

欧拉图与半欧拉图

  • 欧拉图指的是给出的图G<V, E>,满足所有的点V联通,并且每个点的度都是偶数,则图中的所有的边都可以一笔画经过,并且回到起始点,则我们说给出的图G是欧拉图;
  • 半欧拉图的性质与欧拉图相似,但是它可以首尾不相连接,也就是这幅图是欧拉通路,满足图中的所有的边都可以一笔画经过,但是不要求回归起点。

无向图的欧拉图判定与有向图的欧拉图判定

无向图

  设G是连通无向图,则称经过G的每条边一次并且仅一次的路径为欧拉通路;

  如果欧拉通路是回路(起点和终点是同一个顶点),则称此回路是欧拉回路

  具有欧拉回路的无向图G成为欧拉图

有向图

(1)设D是有向图,D的基图连通,则称经过D的每条边一次并且仅有一次的有向路径为 有向欧拉通路

(2)如果有向欧拉通路是有向回路,则称此有向回路为  有向欧拉回路

(3)具有有向欧拉回路的图D称为有向欧拉图

定理(无向图)

 无向图G存在欧拉通路的充要条件是:G为连通图,并且G仅有两个奇度结点(度数为奇数的顶点)或者无奇度结点。

推论(无向图)

(1) 当G是仅有两个奇度结点的连通图时,G的欧拉通路必以此两个结点为端点;

(2)当G是无奇度结点的连通图时,G必有欧拉回路

(3)G为欧拉图(存在欧拉回路)的充分必要条件是  G为无奇度结点的连通图

定理(有向图)

有向图D存在欧拉通路的充要条件是:D为有向图,D的基图连通,并且所有顶点的出度与入度相等;或者  除两个顶点外,其余顶点的出度与入度都相等,而这两个顶点中一个顶点的出度与入度之差为1,另一个顶点的出度与入度之差为-1.

推论(有向图)

(1)当D除出、入度之差为1,-1的两个顶点之外,其余顶点的出度与入度相等时,D的有向欧拉通路必以出、入度之差为1的顶点作为始点,以出、入度之差为-1的顶点作为终点。

(2)当D的所有顶点的出、入度都相等时,D中存在有向欧拉回路。

(3)有向图D为有向欧拉图的充要条件是  D的基图为连通图,并且所有顶点的出、入度都相等。
 


欧拉图(半欧拉图)的路径求解

先举例一张图:

  我们直接从图上来看,我们正确的跑法肯定是“3--1--2--3--4--5--6--4”或者是“4--6--5--4--3--2--1--3”等等很多种方案,我分别列举了输出字典序最小与最大的情况。

  这样的话,我们可以直接dfs,我们直接找到一个起点并且往下跑。但是,这可能存在一个问题,譬如说我们的3号结点,这次想直接跑4号结点,那样就会产生最坏的情况。所以,这告诉了我们如果没有其他路径,最好不要去跑桥(3--4这条边就是桥)。

  我们这里用的是dfs深搜来解决这个问题,这里是dfs加上栈的维护,相当于是起到了一个“修复”的作用。我们这次从3往4号结点跑,因为会回溯回来再重新跑(相当于是更正修复),所以dfs搜到的是“3--4--5--6--4--(回溯)--1--2--3”此时我们的栈会变成:从栈底往栈顶来看“4 6 5 4 3 2 1 3”。此时,就是正确的欧拉通路了。

dfs的函数过程是:

    void dfs(int u)
    {
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            if(vis[i]) continue;
            vis[i] = true;
            v = edge[i].to;
            dfs(v);
        }
        Stap[++Stop] = u;
    }

当然,这个剪枝,虽然是用了vis剪枝了,但是由于每条边只用一次,我们可以用一种叫做当前弧优化的东西来剪枝(可以参考Dinic的当前弧优化来解决这个问题)。

void dfs(int u)
{
    for(int i=head[u], v; ~i; i=head[u])
    {
        v = edge[i].to; head[u] = edge[head[u]].nex;
        dfs(v);
    }
    Stap[++Stop] = u;
}

接下去,换一种比较简短的写法,与上面这个作用一样:

void dfs(int u)
{
    while(~head[u])
    {
        v = edge[i].to; head[u] = edge[head[u]].nex;
        dfs(v);
    }
    Stap[++Stop] = u;
}

放一道判断是否是欧拉回路的题:

欧拉回路

 HDU - 1878

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e3 + 7;
int N, M, du[maxN], root[maxN];
int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
inline void init()
{
    for(int i=1; i<=N; i++)
    {
        du[i] = 0;
        root[i] = i;
    }
}
int main()
{
    while(scanf("%d", &N) && N)
    {
        scanf("%d", &M);
        init();
        for(int i=1, u, v, fu, fv; i<=M; i++)
        {
            scanf("%d%d", &u, &v);
            du[u]++; du[v]++;
            fu = fid(u); fv = fid(v);
            if(fu ^ fv) { root[fu] = fv; }
        }
        bool ok = true; int cnt = 0;
        for(int i=1; i<=N; i++)
        {
            if(i == fid(i))
            {
                cnt++;
                if(cnt > 1)
                {
                    ok = false;
                    break;
                }
            }
            if(du[i] & 1) { ok = false; break; }
        }
        printf("%d\n", ok);
    }
    return 0;
}

再放一道输出欧拉回路路径的题:

Watchcow

 POJ - 2230

给出N个点,M条边,并且呢,M条边要经过两次,一次是从u到v,那么下一次就不能走u到v了,只允许走v到u了。

其实题意已经帮你把欧拉图建好了,而且题目保证联通。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e4 + 7, maxM = 5e4 + 7;
int N, M;
struct LSQXX
{
    int head[maxN], cnt;
    struct Eddge
    {
        int nex, to;
        Eddge(int a=-1, int b=0):nex(a), to(b) {}
    }edge[maxM << 1];
    inline void addEddge(int u, int v)
    {
        edge[cnt] = Eddge(head[u], v);
        head[u] = cnt++;
    }
    inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
    int Stap[maxM << 1], Stop;
    void dfs(int u)
    {
        int v;
        while(~head[u])
        {
            v = edge[head[u]].to;
            head[u] = edge[head[u]].nex;
            dfs(v);
        }
        Stap[++Stop] = u;
    }
    inline void clear()
    {
        cnt = 0; Stop = 0;
        for(int i=1; i<=N; i++) head[i] = -1;
    }
} E;
struct Graph
{
    int u, v;
    Graph(int a=0, int b=0):u(a), v(b) {}
    inline void In_Put() { scanf("%d%d", &u, &v); }
} G[maxM];
int main()
{
    scanf("%d%d", &N, &M);
    E.clear();
    for(int i=1; i<=M; i++) { G[i].In_Put(); E._add(G[i].u, G[i].v); }
    E.dfs(1);
    for(int i=E.Stop; i; i--) printf("%d\n", E.Stap[i]);
    return 0;
}

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值