Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 8

Đường đi và chu trình Euler

ĐỊNH NGHĨA 1 :

- Chu trình đơn trong G đi qua mỗi cạnh của nó một lần được gọi là chu trình Euler.
- Đường đi đơn trong G đi qua mỗi cạnh của nó một lần được gọi là đường đi Euler.
- Đồ thị được gọi là đồ thị thị Euler nếu nó có chu trình Euler, và gọi là đồ thị nửa Euler nếu nó có
đường đi Euler.
- Mọi đồ thị Euler đều là nửa Euler nhưng điều ngược lại không đúng.

Ví dụ 1: xác định đồ thị euler, nửa Euler trong các đồ thị vô hướng dưới đây

Đồ thị G1 là đồ thị Euler vì nó có chu trình Euler a,e,c,d,e,b,a.

Đồ thị G2 không có chu trình cũng như đường đi Euler.

Đồ thị G3 không có chu trình nhưng có đường đi Euler a,c,d,e,b,a,b vì thế G3 là nửa Euler.

Ví dụ 2: Đồ thị có hướng nào dưới đây có chu trình Euler? Trong số những đồ thị không có đồ thị nào có
đường đi Euler
Đồ thị H2 có chu trình Euler : a,g,c,b,g,e,d,f,a. Cả H1 và H3 đều không có chu trình Euler. H3 có đường đi
Euler, cụ thể là c,d,b,c,a,b, nhưng H1 thì không.

- Đồ thị vô hướng liên thông G=(V,E) là đồ thị Euler khi và chỉ khi mọi đỉnh của G đều có bậc chẵn
- Cho đồ thị có hướng G liên thông và có hơn 1 đỉnh. Khi đó, G có chu trình Euler nếu và chỉ nếu G
cân bằng ( deg+(v)=deg-(v) với mọi đỉnh v)

Hệ quả : Đồ thị vô hướng liên thông G=(V,E) là đồ thị nửa Euler khi và chỉ khi nó không có quá hai
đỉnh bậc lẻ. Khi đó tìm được đường đi Euler có hai đầu đường đi Euler có hai đầu đường đi nằm ở
hai đỉnh bậc lẻ.

Ví dụ 3: Đồ thị vô hướng dưới đây có phải đồ thị Euler hay không ? Vì sao ?

Nhận thấy tất cả các đỉnh đều có bậc chẵn nên đây là đồ thị Euler với chu trình Euler là DEABCEBD.

Ví dụ 4: Đồ thị có hướng dưới đây có phải đồ thị Euler hay không ? Vì sao ?
Đồ thị này thỏa mãn deg+(v)=deg-(v) (với mọi đỉnh v) nên đây là đồ thị Euler. Chu trình Euler là
DEABCEBD.

2- Thuật toán Fleury tìm chu trình Euler

Phát biểu bài toán : cho đa đồ thị vô hướng liên thông G gồm n đỉnh, m cạnh. Hãy tìm ra một chu
trình Euler của đồ thị?

Ý tưởng: Xuất phát từ một đỉnh, ta tùy ý theo các cạnh theo hai nguyên tắc :

- Một là xóa bỏ cạnh vừa đi qua.


- Hai là chỉ chọn đi vào cạnh "một đi không trở lại" nếu như không còn
cạnh nào khác để chọn. Việc kiểm tra một cạnh (u,v) có phải là cạnh
"một đi không trở lại" hay không có thể thực hiện bằng cách: Thử xóa
cạnh đó đi rồi dùng BFS để tìm đường đi từ v tới u, nếu không tìm
được thì cạnh đó chắc chắn là cạnh "một đi không trở lại".

Ví dụ 7 :
Với đồ thị trên, nếu xuất phát từ đỉnh 1, có hai cách chọn đỉnh để đi tiếp: sang 2 hoặc 3, giả sử
chọn 2 thì xóa cạnh (1,2) vừa đi qua. Từ chỉ có cách duy nhất là sang 4, nên cho dù (2,4) là cầu ta
cũng phải đi qua và xóa cạnh (2,4). Bây giờ đang đứng ở đỉnh 4 thì ta có 3 cách đi tiếp: sang 3,
sang 5 hoặc sang 6. Vì (4,3) là cầu nên ta sẽ không đi theo cạnh (4,3) mà sẽ đi theo (4,5) hoặc
(4,6). Nếu đi theo (4,5) và cứ tiếp tục đi như vậy, ta sẽ được chu trình Euler là
(1,2,4,5,7,8,6,4,3,1). Còn đi theo (4,6) sẽ được chu trình Euler là (1,2,4,6,8,7,5,4,3,1).

Ta sẽ cài đặt giải thuật bằng cách sử dụng một ma trận kề adj[u][v] để
biểu diễn đồ thị cho thuận tiện khi thực hiện thao tác xóa cạnh, ngoài
ra có thêm một hàm kiểm tra xem đồ thị có phải là đồ thị Euler hay
không.
from collections import deque

def enter():
n = int(input())
adj = [[0] * (n + 1) for _ in range(n + 1)]
deg = [0] * (n + 1)

while True:
try:
u, v, k = map(int, input().split())
adj[u][v] = adj[v][u] = k
deg[u] += k
deg[v] += k
except EOFError:
break

return n, adj, deg

def dfs(n, u, cnt_comps, adj, number):


number[u] = cnt_comps
for v in range(1, n + 1):
if number[v] == 0 and adj[u][v]:
dfs(n, v, cnt_comps, adj, number)

def check_euler_graph(n, adj, deg):


cnt_comps = 0
number = [0] * (n + 1)

for u in range(1, n + 1):


if number[u] == 0:
cnt_comps += 1
dfs(n, u, cnt_comps, adj, number)

if cnt_comps > 1:
return False

for u in range(1, n + 1):


if deg[u] % 2 == 1:
return False

return True

def can_go_back(n, adj, u, v):


adj[u][v] -= 1
adj[v][u] -= 1

is_free = [True] * (n + 1)
path = deque()
path.append(v)

while path:
cur = path.popleft()
if cur == u:
break

for next_v in range(1, n + 1):


if is_free[next_v] and adj[cur][next_v]:
is_free[next_v] = False
path.append(next_v)

adj[u][v] += 1
adj[v][u] += 1
return not is_free[u]

def fleury(n, adj, deg):


if not check_euler_graph(n, adj, deg):
print(0)
return

cur_vertex = 1
next_v = 0
circuit = [cur_vertex]

while True:
next_v = 0
for v in range(1, n + 1):
if adj[cur_vertex][v]:
next_v = v
if can_go_back(n, adj, cur_vertex, next_v):
break

if next_v != 0:
adj[cur_vertex][next_v] -= 1
adj[next_v][cur_vertex] -= 1
circuit.append(next_v)
cur_vertex = next_v
else:
break

for u in circuit:
print(u, end=' ')

if __name__ == "__main__":
n, adj, deg = enter()
fleury(n, adj, deg)

You might also like