算法 {欧拉回路}

本文介绍了有向图和无向图中的欧拉路径与欧拉回路概念,包括半欧拉路径的定义和性质。文章还探讨了如何通过算法找到这些路径,并提供了相关证明和例题,强调了图的结构特征对于存在欧拉路径和回路的重要性。
摘要由CSDN通过智能技术生成

算法 {

欧拉回路(有向图)

定义

#欧拉回路#
给定有向图G, 如果存在一个环x->...->x 满足G中所有的边 都存在于这个环中一次, 那么这个环 称之为欧拉回路;
. 比如G: a<->b<->c, 那么a->b->c->b->a是一个欧拉回路;

#欧拉回路图#
存在欧拉回路的图, 称之为欧拉回路图;
. 结论: 有向图G, 如果{每个点的入度等于出度 | 所有边是连通的}, 那么G是欧拉回路图;

性质

算法

求欧拉回路路径

比如a->b->c->a, b->b, 那么a->b->b->c->a是一个欧拉回路, 你需要依次输出每条边的ID号;

做法是: 暴力DFS, 比如当前是dfs(a) 对于a->b这个边 (令她的ID是X), 我们先把这个边给删除掉 (即让FirstEdge[a] = NextEdge[ X]), 然后dfs(b), 最后回溯时 再ANS.push_back(X); DFS结束后 reverse(ANS)一下;
拿上面例子来说, 比如dfs次序: a1, b1, c, a2, b2, 在c这里回溯时 我们将c->a放入答案, 然后在b1回溯时 放入了b->c, 然后在b1回溯了b->b, 最后在a1回溯了a->b;

auto Dfs = [&]( auto & _dfs, int _cur)->void{
    for( ;_graph.FirstEdge[ _cur] != -1; ){
        auto edgeID = _graph.FirstEdge[ _cur];
        auto nex = _graph.DirectedEdges.at( edgeID).EndPoint;
        _graph.FirstEdge[ _cur] = _graph.NextEdge[ edgeID];
        _dfs( _dfs, nex);
        ANS.push_back( edgeID + 1);
    }
};
for( int i = 0; i < _graph.PointsCount; ++i){
    if( _graph.FirstEdge[ i] != -1){
        Dfs( Dfs, i); break;
    }
}
std::reverse( ANS.begin(), ANS.end());

例题

@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=130304774;
证明给定图是欧拉回路图;

欧拉回路(无向图)

定义

#欧拉回路#
给定无向图G, 如果存在一个环x->...->x 满足G中所有的边 都存在于这个环中一次, 那么这个环 称之为欧拉回路;
. 比如G: a-b-c-a, 那么a-b-c-a (或b-c-a-b 或c-a-b-c)是一个欧拉回路;

#欧拉回路图#
存在欧拉回路的图, 称之为欧拉回路图;
. 结论: 无向图G, 如果{每个点的度数都是偶数 | 所有边是连通的}, 那么G是欧拉回路图;

算法

求欧拉回路路径

有向图求欧拉回路差不多, 只不过 由于是无向边(即a-b边 在代码层面 是由a<->b两条边组成的), 因此 当我们遍历了a->b(她要放到答案里) 那么同时要把她对应的b->aVis=1掉;

std::vector< bool> Vis( _graph.EdgesCount << 1, 0);
auto Dfs = [&]( auto & _dfs, int _cur)->void{
    for( ;_graph.FirstEdge[ _cur] != -1; ){
        auto edgeID = _graph.FirstEdge[ _cur];
        auto nex = _graph.DirectedEdges.at( edgeID).EndPoint;
        _graph.FirstEdge[ _cur] = _graph.NextEdge[ edgeID];
        if( Vis[edgeID] == 0){
            Vis[edgeID] = Vis[edgeID^1] = 1;
            _dfs( _dfs, nex);
            ANS.push_back( edgeID);
        }
    }
};
for( int i = 0; i < _graph.PointsCount; ++i){
    if( _graph.FirstEdge[ i] != -1){
        Dfs( Dfs, i); break;
    }
}
std::reverse( ANS.begin(), ANS.end());

术语

有向图

Given a Directed-Graph G = ( V , E ) G =(V, E) G=(V,E)

An Semi-Eulerian-Path (半欧拉路径) is a path (i.e., a sequence of edges satisfying any two consecutive-edges share a same point) such that the start-point and end-point of the path are distinct, and every edge e ∈ E e \in E eE occurs in the path exactly once;
+ The path would be the form s → . . . → t    s ≠ t s \to ... \to t \ \ s\neq t s...t  s=t, from the start-point s s s, you can traverse the graph with passing though every edge exactly once, and finally arrived at t t t.
+ The edges set of a Semi-Eulerian-Path equals to E E E;
+ The path focuses on the edges, not points (i.e., it would not pass though a Isolated-Point which has no adjacent-edges; a point maybe passed multiple times);

G G G is called Semi-Eulerian (半欧拉图) if it has an Semi-Eulerian-Path;

--

An Eulerian-Circuit (欧拉回路) is an Semi-Eulerian-Path in which the restriction s ≠ t s \neq t s=t replaced by s = t s = t s=t.
+ The path would be the form s → . . . → s s \to ... \to s s...s, from the start-point s s s, you can traverse the graph with passing though every edge exactly once, and finally arrived at s s s.

G G G is called Eulerian (欧拉图) if it has an Eulerian-Circuit;

--

An Eulerian-Path (欧拉路径) is either an Semi-Eulerian-Path or an Eulerian-Circuit.

无向图

Given a Undirected-Graph G = ( V , E ) G =(V, E) G=(V,E)

An Semi-Eulerian-Path (半欧拉路径) is a path (i.e., a sequence of edges satisfying any two consecutive-edges share a same point;) such that the start-point and end-point of the path are distinct, and every edge e ∈ E e \in E eE occurs in the path exactly once;
+ The path would be the form s − . . . − t    s ≠ t s - ... - t \ \ s\neq t s...t  s=t, from the start-point s s s, you can traverse the graph with passing though every edge exactly once, and finally arrived at t t t.
. Note that an edge in a Undirected-Path, in fact it is Directed; e.g., a − b a-b ab in the path denotes from a a a to b b b; although an Undirected-Edge a − b a-b ab has two possible directions ( a → b a\to b ab or b → a b\to a ba), but the direction of any edge a − b a-b ab is unique in a path.
+ The edges set of a Semi-Eulerian-Path equals to E E E;
+ The path focuses on the edges, not points (i.e., it would not pass though a Isolated-Point which has no adjacent-edges; a point maybe passed multiple times);

G G G is called Semi-Eulerian (半欧拉图) if it has an Semi-Eulerian-Path;

--

An Eulerian-Circuit (欧拉回路) is an Semi-Eulerian-Path in which the restriction s ≠ t s \neq t s=t replaced by s = t s = t s=t.
+ The path would be the form s → . . . → s s \to ... \to s s...s, from the start-point s s s, you can traverse the graph with passing though every edge exactly once, and finally arrived at s s s.

G G G is called Eulerian (欧拉图) if it has an Eulerian-Circuit;

--

An Eulerian-Path (欧拉路径) is either an Semi-Eulerian-Path or an Eulerian-Circuit.

半欧拉路径

有向图

Proposition-A: G G G has a Semi-Eulerian-Path (i.e., G G G is Semi-Eulerian-Graph)

Proposition-B:
1 There is one and only one start-point s s s satisfying O u t [ s ] − I n [ s ] = 1 Out[s] - In[s] = 1 Out[s]In[s]=1; and,
2 There is one and only one end-point t t t satisfying I n [ t ] − O u t [ t ] = 1 In[t] - Out[t] = 1 In[t]Out[t]=1; and,
3 I n [ x ] = O u t [ x ] ∀ x ∈ V / { s , t } In[x] = Out[x] \quad \forall x \in V/\{s,t\} In[x]=Out[x]xV/{s,t}; and,
4 s s s can reach to all Non-Isolated-Points ( a a a is Isolated-Point if and only if I n [ x ] = O u t [ x ] = 0 In[x]=Out[x] = 0 In[x]=Out[x]=0);

Conclusion: A = B A = B A=B

Proof:

A → B A \to B AB: From the definition of a Semi-Eulerian-Path, it is obviously proved;

B → A B \to A BA: Link

算法

path = a Semi-Eulerian-Path of the Graph;

void Dfs( int a){
	for( `a->b` : all adjacent-edges of `a`){
		auto cur_edge = `a->b`; //< a copy
		delete this edge `a->b` from `G`;
		Dfs( b);
		ASSERT( `b` is Isolated (i.e., `b` has no adjacent-edges));
		path.push_back( cur_edge);
	}
}

Dfs( s);
inverse( path);

Annotation:

Firstly, we get a Limited-Path of s s s, denoted P P P, suppose that it is s → . . . → t s \to ... \to t s...t, let P P P be the sequence of all the points it passed by (i.e., S = [ s , . . . , t ] S = [s,...,t] S=[s,...,t]);
+ S S S may contain multiple identical points (e.g., s , t , s , t , a , t s,t,s,t,a,t s,t,s,t,a,t); a Non-Isolated-Point may not occurs in the path (e.g., the Semi-Eulerian-Graph ( s → t ) ( s → a ) ( a → s ) (s\to t) (s\to a) (a\to s) (st)(sa)(as), where s → t s\to t st is a Limited-Path);

Secondly, note that, the current G G G has been changed, all edges of P P P has been removed from G G G;

set<> new_isolated;
for( `a` : $(the reversed `S`)){ //< `a = [t,...,s]`
	p = the Limited-Path of `a` in `G`;
	ASSERT( `p` is the form `[a,...,a]`); //< `p = [a]` is also valid
	if( `p` in `new_isolated`){
		ASSERT( `p` has no edge);
	}
	ASSERT( all points in `p` except `a`, would not occurs in `new_isolated`);
	$(Remove all edges in `p` from `G`);
	for( `b` : all points in `p`){
		new_isolated.insert( b);
	}
}
ASSERT( $(all Non-Isolated-Points in the Original `G`) would equals to `new_isolated`); //< also means `no $(Isolated-Points in the Original `G`) would occurs in `new_isolated`);

We call p is a Loop if p contains edges;
. t would not has Loop, cuz it is already Isolated after the First-Step;

The Limited-Path `P` of `s,t`
s, s, t, t, a, c, d, b, a, d, b, c, b, t, d, t, t, t
                                 |        |
                                Sc        Sd

Sd = (d, b, s, b, s, d, b, d) //< must no `t`
Sc = (c, a, a, c, a, c) //< must no `t,d,b,s`
ASSERT( all others points except `c,d`, has no loop); //< note that, the above two `c,c` are distinct points in this context, cuz a point denotes a `Dfs(x)`, two same points can denotes different Dfs;

For the above illustration, we found a Eulerian-Path s,s,...,d,b, (Sc), b,t, (Sd), t,t,t, so our goal is to get it.
But the DFS-sequence is P , S d , S c P, Sd, Sc P,Sd,Sc which is not a Eulerian-Path; so, the algorithm is a bit of sophistication, it used a Property:
Once the DFS is back-tracking, that is:

Dfs( int a){
	for( `a->b` : all adjacent-edges of `a`){
		auto cur_edge = `a->b`; //< a copy
		delete this edge `a->b` from `G`;
		Dfs( b);
		//>< @Loc_0
		path.push_back( cur_edge);
	}
}

When we at @Loc_0 (i.e., Dfs(b) is finished), currently b b b must be Isolated; cuz once DFS is back-tracking, it means it has found a Limited-Path, whatever it occurs in P P P or S d , S c Sd, Sc Sd,Sc;
Proof:
1 if a -> b (i.e., Dfs(a) -> Dfs(b)) is in P P P, from above we know that b must in new_isolated;
2 if a -> b (i.e., Dfs(a) -> Dfs(b)) is in a Loop (e.g., S d Sd Sd), once the DFS is finished (i.e., the whole Limited-Path has been deleted), all points in the Loop must in new_isolated;

So, we perform path.push_back( cur_edge); at @Loc_0, we would get t,t,t, (~Sd), t, b, (~Sc), b, d, ..., s, s whose reverse is the answer (Eulerian-Path);

性质

+ The Eulerian-Path (either Semi-, or Circuit) itself is also a Limited-Path;

例题

+ AcWing-1185. 单词游戏

无向图

Proposition-A: G G G has a Semi-Eulerian-Path (i.e., G G G is Semi-Eulerian-Graph)

Proposition-B:
1 There is two and only two points s , t s,t s,t satisfying D e g [ s ] = D e g [ t ] = O d d Deg[s] = Deg[t] = Odd Deg[s]=Deg[t]=Odd; and,
2 D e g [ x ] = E v e n ∀ x ∈ V / { s , t } Deg[x] = Even \quad \forall x \in V/\{s,t\} Deg[x]=EvenxV/{s,t}; and,
3 s s s can reach to all Non-Isolated-Points ( a a a is Isolated-Point if and only if D e g [ x ] = 0 Deg[x] = 0 Deg[x]=0);

Conclusion: A = B A = B A=B

Proof:

A → B A \to B AB: From the definition of a Semi-Eulerian-Path, it is obviously proved;

B → A B \to A BA: Link

性质

+ A Semi-Eulerian-Path s − . . . − t s - ... - t s...t, its Converse-Path (i.e., an edge a − b a-b ab in the previous-path, becomes b − a b-a ba) t − . . . − s t-...-s t...s is also a Semi-Eulerian-Path.

例题

+ 1124. 骑马修栅栏

算法

+ Similar to the Algorithm above for the Directed-Graph;
+ Link-The-Technique-for-Deleting-Edges;

欧拉回路

有向图

Proposition-A: G G G has a Eulerian-Circuit (i.e., G G G is Eulerian-Graph)

Proposition-B:
1 I n [ x ] = O u t [ x ] ∀ x ∈ V In[x] = Out[x] \quad \forall x \in V In[x]=Out[x]xV; and,
2 Let x x x be a Non-Isolated-Points, then x x x can reach to all Non-Isolated-Points ( a a a is Isolated-Point if and only if I n [ x ] = O u t [ x ] = 0 In[x]=Out[x] = 0 In[x]=Out[x]=0);

Conclusion: A = B A = B A=B

Proof:

A → B A \to B AB: From the definition of a Semi-Eulerian-Path, it is obviously proved;

B → A B \to A BA: Link

性质

+ Eulerian-Graph is the SCC of itself;

例题

+ AcWing-1123. 铲雪车

算法

Similar to the Algorithm-for-Semi-Eulerian-Path;

无向图

Proposition-A: G G G has a Eulerian-Circuit (i.e., G G G is Eulerian-Graph)

Proposition-B:
1 D e g [ x ] = E v e n ∀ x ∈ V Deg[x] = Even \quad \forall x \in V Deg[x]=EvenxV; and,
2 Let x x x be a Non-Isolated-Points, then x x x can reach to all Non-Isolated-Points ( a a a is Isolated-Point if and only if D e g [ x ] = 0 Deg[x] = 0 Deg[x]=0);

Conclusion: A = B A = B A=B

Proof:

A → B A \to B AB: From the definition of a Semi-Eulerian-Path, it is obviously proved;

B → A B \to A BA: Link

算法

Similar to the Algorithm-for-Semi-Eulerian-Path;

例题

+ AcWing-1184. 欧拉回路

证明

半欧拉路径-有向图

--

A Directed-Graph G = ( V , E ) G = (V,E) G=(V,E) satisfying:
1 There is one and only one start-point s s s satisfying O u t [ s ] − I n [ s ] = 1 Out[s] - In[s] = 1 Out[s]In[s]=1; and,
2 There is one and only one end-point t t t satisfying I n [ t ] − O u t [ t ] = 1 In[t] - Out[t] = 1 In[t]Out[t]=1; and,
3 I n [ x ] = O u t [ x ] ∀ x ∈ V / { s , t } In[x] = Out[x] \quad \forall x \in V/\{s,t\} In[x]=Out[x]xV/{s,t};

Conclusion: The end-point of the Limited-Path of s s s must be t t t;

Proof:
+ For the point s s s: Initially, O u t [ s ] = I n [ s ] + 1 Out[s]=In[s]+1 Out[s]=In[s]+1 when we at s s s, once we leave it, it becomes O u t [ s ] = I n [ s ] Out[s] = In[s] Out[s]=In[s];
. Then, once we arrived at s s s, now O u t [ s ] = I n [ s ] − 1 Out[s] = In[s] - 1 Out[s]=In[s]1, it means I n [ s ] ≥ 1 In[s] \geq 1 In[s]1, so it must traverse further to the next-point;
. As a result, s s s would not be the end-point of the Limited-Path of s s s;
+ For these points x ∈ V / { s , t } x \in V/ \{s,t\} xV/{s,t}: Initially, O u t [ x ] = I n [ x ] Out[x]=In[x] Out[x]=In[x];
. Then, once we arrived at x x x, now O u t [ x ] = I n [ x ] − 1 Out[x] = In[x] - 1 Out[x]=In[x]1, it means I n [ s ] ≥ 1 In[s] \geq 1 In[s]1, so it must traverse further to the next-point;
. As a result, x x x would not be the end-point of the Limited-Path of s s s;

--

Let P P P be a Limited-Path s → . . . → t s \to ... \to t s...t, and G 1 = ( V , E 1 ) G1 = (V,E1) G1=(V,E1) where E 1 = E − P E1 = E - P E1=EP denoted all edges of P P P has been deleted from E E E.

Conclusion: Iterating all points p ∈ P p \in P pP, find the Limited-Path of p p p in G 1 G1 G1, p → . . . → p 1 p \to ... \to p1 p...p1 where p p p would equals p 1 p1 p1, then deletes there edge from G 1 G1 G1, finally, G 1 G1 G1 would has no edges;

Proof-1: p = p 1 p = p1 p=p1;
+ Cuz G 1 G1 G1 satisfying I n [ x ] = O u t [ x ] ∀ x In[x] = Out[x] \quad \forall x In[x]=Out[x]x, the Limited-Path of any point a a a must be the form a → . . . → a a \to ... \to a a...a;

Proof-2: G 1 G1 G1 would no edges finally;
+ Disproof: if there is edge a → b a\to b ab not be deleted, that shows ∀ v ∈ P \forall v \in P vP cannot reach to a a a, then s s s cannot reach to a a a; this contradicts the assumption s s s can reach to all Non-Isolated-Points;

----

半欧拉路径-无向图

欧拉回路-有向图

Similar to Link

欧拉回路-无向图

----

Redirected-Id: 4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值