Tải bản đầy đủ (.doc) (30 trang)

Tiểu luận môn toán ứng dụng Chu trình Euler và bài toán người đưa thư

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (384.29 KB, 30 trang )

MỤC LỤC
CHƯƠNG 1. ĐẠI CƯƠNG VỀ ĐỒ THỊ 4
CHƯƠNG 2. CHU TRÌNH EULER VÀ BÀI TOÁN NGƯỜI ĐƯA THƯ 8
2.1. Định nghĩa 8
2.2. Điều kiện cần và đủ 8
2.2.1. Định lý 1 (Định lý Euler) 8
2.3. Các thuật toán tìm chu trình Euler 10
2.4. Thiết kế cấu trúc dữ liệu và giải thuật tìm đường đi, chu trình Euler 12
2.5. Bài toán người đưa thư 14
3.1. Tìm đường đi, chu trình Euler 18
3.2. Giải bài toán người đưa thư 21
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
GIỚI THIỆU
Lý thuyết đồ thị là ngành khoa học được phát triển từ lâu nhưng lại có
nhiều ứng dụng hiện đại. Những ý tưởng cơ bản của nó được đưa ra từ thế
kỷ XVIII bởi nhà toán học Thụy sĩ tên là Leonhard Euler.
Lý thuyết đồ thị được dùng để giải các bài toán trong nhiều lĩnh vực
khác nhau. Chúng ta có thể xác định hai máy tính trong mạng có thể trao
đổi thông tin được với nhau hay không nhờ mô hình đồ thị của mạng máy
tính. Đồ thị có trọng số trên các cạnh có thể sử dụng để giải các bài toán
như: Tìm đường đi ngắn nhất giữa hai thành phố trong mạng giao thông.
Chúng ta cũng còn sử dụng đồ thị để giải các bài toán về lập lịch, thời khóa
biểu, và phân bố tần số cho các trạm phát thanh và truyền hình…
Ngày nay Lý thuyết đồ thị đã phát triển thành một ngành Toán học
ứng dụng có vị trí đặc biệt quan trọng về mặt lý thuyết cũng như ứng dụng.
Trong nội dung cuốn tiểu luận này nhóm chúng tôi xin trình bày về
“Chu trình Euler và bài toán người đưa thư”.
Đồng Hới, tháng 05 năm 2012
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
- Đề tài: Chu trình Euler và bài toán người đưa thư


- Nhóm: 6 người
STT Họ tên
Công việc
(theo mục
lục)
Chữ ký
Nhận xét của
giáo viên
1 Nguyễn Trần Sỹ Chương 1
2 Võ Phi Thanh 2.12.4
3 Nguyễn Thị Hà Phương 2.5
4 Hoàng Công Tiến 2.5
5 Hoàng Đình Tuyền Chương 3
6 Nguyễn Duy Linh Chương 3
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
CHƯƠNG 1. ĐẠI CƯƠNG VỀ ĐỒ THỊ
1.1 Giới thiệu
Lý thuyết đồ thị là một lĩnh vực nghiên cứu đã có từ lâu và có nhiều ứng
dụng trong ngành công nghệ thông tin. Những tư tưởng cơ bản của lý thuyết đồ
thị được đề xuất vào những năm đầu của thế kỷ 18 bởi nhà toán học lỗi lạc người
Thụy Sỹ: Leonhard Euler. Chính ông là người đã sử dụng đồ thị để giải bài toán
nổi tiếng về 7 cái cầu ở thành phố Konigberg.
Những ứng dụng cơ bản của đồ thị như:
- Xác định tính liên thông trong một mạng máy tính: hai máy tính nào đó có thể
truyền dữ liệu cho nhau được không.
- Tìm đường đi ngắn nhất trên mạng giao thông
- …
1.2 Định nghĩa đồ thị
Định nghĩa 1.2.1. Một đơn đồ thị vô hướng là một bộ G=<V,E>, trong đó:

- V ≠ Ø là tập hợp hữu hạn gồm các đỉnh của đồ thị.
- E là tập hợp các cặp không có thứ tự gồm hai phần tử khác nhau của V gọi
là các cạnh.
Như vậy, theo định nghĩa trên, trong một đơn đồ thị không thể có các cặp
cạnh nối cùng một cặp đỉnh (do E là tập hợp nên không thể có 2 cặp trùng nhau),
các cạnh đều không phân biệt thứ tự nên cạnh [u,v] và cạnh [v,u] đều được coi là
một cạnh duy nhất, điều này phù hợp với việc biểu diễn các con đường 2 chiều,
và hiển nhiên là không có cặp [u,u] nào đó trong E.
Ví dụ 1.1.




Định nghĩa 1.2.2. Đa đồ thị vô hướng là một bộ G=<V,E>, trong đó
- V ≠ Ø là tập hợp hữu hạn gồm các đỉnh của đồ thị.
- E là một họ các cặp không có thứ tự của V gọi là các cạnh.
Định nghĩa 1.2.3. Đơn đồ thị có hướng là một bộ G=<V,E>, trong đó:
- V ≠ Ø là tập hợp hữu hạn gồm các đỉnh của đồ thị.
- E là tập hợp các cặp có thứ tự gồm hai phần tử khác nhau của V gọi là
các cung.
Ví dụ:
Tiểu luận nhóm 1 Trang
a. Đơn đồ thị
vô hướng
b. Không phải đơn
đồ thị vô hướng do
có các cặp cạnh nối
cùng một cặp đỉnh
c. Không phải đơn đồ
thị vô hướng do có

cạnh nối một đỉnh với
chính nó.
Toán ứng dụng Chu trình Euler và bài toán người đưa thư

Định nghĩa 1.2.4. Đa đồ thị có hướng là một bộ G=<V,E>, trong đó
- V ≠ Ø là tập hợp hữu hạn gồm các đỉnh của đồ thị.
- E là một họ các cặp có thứ tự của V gọi là các cung.
Các cung nối cùng một cặp đỉnh được gọi là các cung song song.
Ví dụ:

Chú ý:
- Đồ thị sau vẫn được coi là đơn đồ thị có hướng vì e1và e2, e3 và e4 không phải
là 2 cung song song (do khác hướng).

1.3. Một số thuật ngữ cơ bản
Định nghĩa 1.3.1. Cho đồ thị vô hướng G=<V,E>.
- Hai đỉnh u và v của đồ thị được gọi là kề nhau nếu (u,v) là một cạnh của đồ
thị.
- Nếu e=(u,v) là cạnh của đồ thị thì ta nói cạnh này là liên thuộc với hai đỉnh u
và v. Cạnh được nói là nối đỉnh u và v. Đỉnh u và v được gọi là đỉnh đầu của
cạnh e.
Định nghĩa 1.3.2. Cho đồ thị vô hướng G=<V,E>. Bậc của đỉnh v trong đồ thị,
ký hiệu là deg(v), là số cạnh liên thuộc với nó. Đỉnh có bậc 0 được gọi là đỉnh cô
lập, đỉnh có bậc 1 gọi là đỉnh treo.
Định nghĩa 1.3.3 Cho đồ thị có hướng G=<V,E>.
- Hai đỉnh u và v của đồ thị được gọi là kề nhau nếu (u,v) là một cung của đồ thị.
- Nếu e=(u,v) là cung của đồ thị thì ta nói cung này đi ra khỏi đỉnh u vào đi vào
đỉnh v. Đỉnh u được gọi là đỉnh đầu của cung e và đỉnh v được gọi là đỉnh cuối
của cung e.
1.4. Đường đi, chu trình và đồ thị liên thông

Định nghĩa 1.4.1. Cho đồ thị *G = <V,E> (* ký hiệu cho dùng chung cả đồ thị
vô hướng và có hướng). Đường đi độ dài n từ đỉnh u đến đỉnh v (n là số nguyên
dương) là dãy:
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư

trong đó
* Ta sẽ dùng thuật ngữ đồ thị để chỉ chung cho cả đồ thị vô hướng và đồ thị có
hướng Đường đi nói trên còn có thể được biểu diễn bằng dãy các cạnh/cung:

Đỉnh u gọi là đỉnh đầu của đường đi, đỉnh v gọi là đỉnh cuối của đường đi.
Đường đi có đỉnh đầu và đỉnh cuối trùng nhau (u=v) gọi là chu trình.
Ví dụ: Cho đồ thị vô hướng sau:

Một số đường đi từ đỉnh 2 đến đỉnh 7:
- Đường đi d1: 2 3 4 7 (đường đi độ dài 3)
- Đường đi d2: 2 3 4 1 3 6 7 (đường đi độ dài 6)
Một số chu trình trên đồ thị trên:
- Chu trình C1: 1 2 3 1 (chu trình có độ dài 3)
- Chu trình C2: 1 2 3 7 6 3 4 1 (chu trình có độ dài 7)
Định nghĩa 1.4.2. Đồ thị vô hướng G = <V,E> được gọi là liên thông nếu luôn
tìm được đường đi giữa hai đỉnh bất kỳ của nó.
Ví dụ: Xét các đồ thị vô hướng sau:


Trong 2 đồ thị trên thì G1là đồ thị liên thông, còn G2 không phải là đồ thị liên
thông vì giữa hai đỉnh 1 và 2 không tồn tại một đường đi nào.
Định nghĩa 1.4.3 Cho đồ thị G = (V,E). Đồ thị H = <W,F> được gọi là đồ thị
con của G nếu và chỉ nếu W € V và F € E.
Trong trường hợp một đồ thị vô hướng G không liên thông, nó sẽ được phân

thành các đồ thị con độc lập nhau và chúng đều liên thông. Mỗi đồ thị con như
vậy được gọi là một thành phần liên thông của G.
Định nghĩa 1.4.4 Cho đồ thị vô hướng G = <V,E>. Đỉnh v của đồ thị được gọi là
đỉnh rẽ nhánh nếu việc loại bỏ v và các cạnh liên thuộc với nó ra khỏi đồ thị sẽ
làm tăng số thành phần liên thông của đồ thị. Cạnh e của đồ thị được gọi là cầu
nếu việc loại bỏ nó ra khỏi đồ thị sẽ làm tăng số thành phần liên thông của đồ thị.
Ví dụ: Xét đồ thị sau:
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư

Trong đồ thị trên, đỉnh 2 là đỉnh rẽ nhánh vì việc loại đỉnh này cùng với các cạnh
(2,3), (2,1), (2,6) sẽ làm đồ thị có 2 thành phần liên thông. Cạnh (2,3) là cầu. Các
cạnh còn lại đều không phải là cầu. Đối với đồ thị có hướng khái niệm liên thông
khó thỏa mãn hơn do các cung bị hạn chế về chiều. Từ đó, bên cạnh khái niệm
liên thông như đề cập trong đồ thị vô hướng, ta sẽ đưa thêm khái niệm liên thông
nhẹ hơn: liên thông yếu.
Định nghĩa 1.4.5. Cho G = <V,E> là đồ thị có hướng.
a. G được gọi là liên thông mạnh nếu luôn tìm được đường đi giữa hai đỉnh bất
kỳ của nó.
b. G được gọi là liên thông yếu nếu đồ thị vô hướng tương ứng với nó (đồ thị vô
hướng có được bằng cách biến các cung một chiều thành các cạnh hai chiều) là
đồ thị vô hướng liên thông.
1.5 Biểu diễn đồ thị trên máy tính
Định nghĩa 1.5.1.1. Cho đồ thị G = <V,E>, với tập đỉnh V = {v1, v2, , vn}. Ta
gọi ma trận kề của đồ thị là ma trận A, kích thước nxn được xác định như sau:

Ví dụ:
a. Xét đồ thị vô hướng sau:

Ma trận kề của đồ thị trên sẽ là:


Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
CHƯƠNG 2.
CHU TRÌNH EULER VÀ BÀI TOÁN NGƯỜI ĐƯA THƯ
2.1. Định nghĩa
Cho đồ thị G=(V,E), V là tập hợp các đỉnh, E là tập hợp các cạnh
Chu trình Euler là chu trình qua mọi cạnh và mọi đỉnh đồ thị, mỗi cạnh
không đi quá 1 lần.
Đường đi Euler là đường đi qua mọi cạnh và mọi đỉnh đồ thị, mỗi cạnh
không đi quá 1 lần.
Cho đồ thị có hướng G=(V,E).
Chu trình có hướng Euler là chu trình có hướng qua mọi cung và mọi đỉnh
đồ thị, mỗi cung không đi quá 1 lần.
Đường đi có hướng Euler là đường đi có hướng qua mọi cung và mọi đỉnh
đồ thị, mỗi cung không đi quá 1 lần.
Đồ thị chứa chu trình Euler gọi là Đồ thị Euler.
2.2. Điều kiện cần và đủ
2.2.1. Định lý 1 (Định lý Euler)
Đồ thị G có chu trình Euler khi và chỉ khi G liên thông và mọi đỉnh có bậc
chẵn khác 0.
Chứng minh
(i) (⇒): Giả sử G có chu trình Euler và v là đỉnh bất kỳ của G. Khi đó chu
trình Euler đến v theo cạnh e thì ra khỏi v bằng cạnh e’ ≠ e. Do đó bậc của v phải
là số chẵn. G hiển nhiên là liên thông.
(ii) (⇐): Giả sử G liên thông và mọi đỉnh có bậc chẵn khác 0. Ta chứng
minh G có chu trình Euler quy nạp theo số cạnh m của G.
• m = 1: Vì G liên thông và mọi đỉnh bậc chẵn nên G chỉ có 1 đỉnh và 1
khuyên. Khuyên đó cũng tạo thành chu trình Euler.
• Giả sử G có m cạnh, số đỉnh n > 0 và mọi đồ thị liên thông có số cạnh nhỏ

hơn m với mọi đỉnh bậc chẵn đều có chu trình euler.
+ Trường hợp n = 1 hoặc 2 thì hiển nhiên tồn tại chu trình Euler .
+ Trường hợp n > 2. Vì bậc của các đỉnh chẵn ≥ 2, bao giờ cũng chọn được
3 đỉnh a, b, c với các cạnh x = (a,b), y = (a,c).
- Giả sử G chứa cạnh z = (b,c).
Xét đồ thị G’ thu được từ G bằng cách loại bỏ ba cạnh x,y,z. Sẽ xảy ra 1
trong ba khả năng sau:
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
 G’ liên thông. Vì số cạnh của G’ nhỏ hơn m và các đỉnh vẫn có bậc chẵn
nên theo giả thiết quy nạp tồn tại chu trình Euler C’ của G’. Nối chu trình con
(x,y,z) với C’ ta thu được chu trình Euler C của G.
 G’ có 2 thành phần liên thông G
1
và G
2
. Không mất tính tổng quát giả
sử G
1
chứa a, G
2
chứa b và c. G
1
có chu trình Euler C
1
, G
2
có chu trình Euler
C
2

. Ta xây dựng chu trình Euler C của G như sau. Xuất phát từ đỉnh a đi theo chu
trình C
1
quay về a, sau đó đi theo cạnh x = (a,b) đến đỉnh b, từ b đi theo chu trình
C
2
quay về b, sau đó đi theo cạnh z = (b,c) và y = (c,a) quay về a.
2.2.2. Định lý 2
Cho đồ thị G có k đỉnh bậc lẻ. Khi đó số đường đi tối thiểu phủ G là k/2.
Chứng minh.
Ta đã biết số đỉnh bậc lẻ là chẵn, k=2n. Chứng minh quy nạp theo n.
(i) n=1: Nối 2 đỉnh bậc lẻ với nhau bằng cạnh z ta thu được đồ thị G’
thoả định lý Euler. Như vậy G’ có chu trình Euler C’. Bỏ cạnh z trên C’ ta thu
được đường đi Euler phủ G.
(ii) Giả sử G có số đỉnh bậc lẻ là 2n và định lý đúng với k<2n. Nối 2
đỉnh bậc lẻ a,b nào đó với nhau bằng cạnh z ta thu được đồ thị G’ có 2n-2 đỉnh
bậc lẻ. Theo giả thiết quy nạp G’ có n-1 đường đi phủ G’. Gọi P là đường đi qua
cạnh z. Hiển nhiên a, b không phải đỉnh đầu hoặc cuối của P, vì vậy nếu bỏ cạnh
z ta thu được 2 đường đi P
1
và P
2
cùng với n-2 đường đi còn lại phủ đồ thị G.
(đpcm)
Bây giờ xét đồ thị có hướng G = (V, A). Ký hiệu
R = {u ∈ V : d
I
(v) = d
O
(v)}

S = {u ∈ V : d
I
(v) > d
O
(v)}
T = {u ∈ V : d
I
(v) < d
O
(v)}
Từ bổ đề bắt tay ta có


∈Vv
O
vd )(
=

∈Vv
I
vd )(

( )



Sv
OI
vdvd )()(
=

( )



Tv
IO
vdvd )()(
Ta ký hiệu
k =
( )



Sv
OI
vdvd )()(
=
( )



Tv
IO
vdvd )()(
2.2.3. Định lý 3
Tiểu luận nhóm 1 Trang
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
(i) Đồ thị có hướng G có chu trình có hướng Euler khi và chỉ khi G liên
thông yếu và mọi đỉnh có nửa bậc vào bằng nửa bậc ra, tức S = ∅ và T = ∅
(ii) Nếu S ≠ ∅, thì số đường đi có hướng tối thiểu phủ G là k. Các đường

đi này nối các đỉnh của T đến các đỉnh của tập S.
Ví dụ: Đồ thị
Đồ thị trên có chu trình Euler: (A,B,C,D,A)
2.3. Các thuật toán tìm chu trình Euler
2.3.1. Thuật toán 1
+ Đầu vào. Đồ thị G ≠ ∅, không có đỉnh cô lập.
+ Đầu ra . Chu trình Euler C của G, hoặc kết luận G không có chu trình Euler.
+ Phương pháp.
(1) Xuất phát: Đặt H := G, k := 1, C := ∅. Chọn đỉnh v ∈ G bất kỳ.
(2) Xuất phát từ v, xây dựng chu trình bất kỳ C
k
trong H.
Nếu tồn tại C
k
, nối C
k
vào C, C := C ∪ C
k
. Sang bước (3)
Nếu không tồn tại C
k
, thì kết luận không có chu trình Euler, kết thúc.
(3) Loại khỏi H chu trình C
k
. Nếu H chứa các đỉnh cô lập. thì loại chúng
khỏi H. Sang bước (4)
(4) Nếu H = ∅, thì kết luận C là chu trình Euler, kết thúc. Ngược lại
sang bước (5)
(5) Nếu H và C không có đỉnh chung, thì kết luận không có chu trình
Euler, kết thúc.

Nếu H và C có đỉnh chung. Chọn v là đỉnh chung của H và C. Đặt k :=
k+1. Quay lại bước (2).
Ví dụ
Cho G là đồ thị Thanh mã tấu Mohammed.
Tiểu luận nhóm 1 Trang 10
e
f
k
h
i
j
g
c
d
b
a
A
B
C
D
Toán ứng dụng Chu trình Euler và bài toán người đưa thư


Ta áp dụng thuật toán 1 để tìm chu trình Euler.
(1) Đặt H := G, k := 1, C := ∅, v := f.
(2) Ta xây dựng chu trình C
1
trong H:
C
1

= (f,g,k,h,i,e,b,c,d,f)
Đặt C = C ∪ C
1
= (f,g,k,h,i,e,b,c,d,f)
(3) Loại C
1
ra khỏi H, ta được đồ thị H như sau
Các đỉnh c và k là các đỉnh cô lập, vì thế ta loại chúng ra khỏi H và nhận
được đồ thị H sau
(5) Chọn đỉnh chung của H và C là v := f. Đặt k := k+1 = 2. Quay lại bước
(2)
(2) Ta xây dựng chu trình C
2
trong H:
C
2
= (f,i,j,h,g,d,b,a,e,f)
Nối C
2
vào C ta được chu trình C sau
C = C ∪ C
2
= (f,g,k,h,i,e,b,c,d,f) ∪ (f,i,j,h,g,d,b,a,e,f)
= (f,g,k,h,i,e,b,c,d,f,i,j,h,g,d,b,a,e,f)
(3) Loại C
2
ra khỏi H, ta được đồ thị H gồm toàn các đỉnh cô lập. Loại
nốt các đỉnh cô lập ta có H = ∅.
(4) Vì H = ∅, ta kết luận C là chu trình Euler, kết thúc.
2.3.2. Thuật toán 2 (Fleury)

Tiểu luận nhóm 1 Trang 11
e
f
h
i
j
g
d
b
a
e
f
k
h
i
j
g
c
d
b
a
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
+ Đầu vào. Đồ thị G ≠ ∅, không có đỉnh cô lập.
+ Đầu ra . Chu trình Euler C của G, hoặc kết luận G không có chu trình
Euler.
+ Phương pháp.
(1) Chọn đỉnh xuất phát bất kỳ v
0
. Đặt v
1

:= v
0
, C := (v
0
). H := G.
(2) Nếu H = ∅, thì kết luận C là chu trình Euler, kết thúc. Ngược lại
sang bước (3).
(3) Chọn cạnh đi tiếp:
- Trường hợp đỉnh v
1
là đỉnh treo: Tồn tại duy nhất đỉnh v
2
kề v
1
.
Chọn cạnh (v
1
, v
2
). Sang bước (4).
- Trường hợp đỉnh v
1
không là đỉnh treo:
Nếu mọi cạnh liên thuộc v
1
là cầu, thì không có chu trình Euler, kết thúc.
Ngược lại, chọn cạnh (v
1
, v
2

) bất kỳ không phải là cầu trong H. Thêm vào
đường đi C đỉnh v
2
. Sang bước (4).
(4) Xoá cạnh vừa đi qua, và xoá đỉnh cô lập:
Loại khỏi H cạnh (v
1
, v
2
). Nếu H có đỉnh cô lập, thì loại chúng khỏi H.
Đặt v
1
:= v
2
. Sang bước (2).
Ví dụ
Cho G là đồ thị hình sau
Đồ thị liên thông và có các đỉnh bậc chẵn. Ta có chu trình Euler sau
(v
6
, v
4
, v
7
, v
5
, v
1
, v
3

, v
4
, v
2
, v
1
, v
4
, v
5
, v
2
, v
3
, v
6
)
2.4. Thiết kế cấu trúc dữ liệu và giải thuật tìm đường đi, chu trình Euler
Chương trình được thiết kế bằng ngôn ngữ tựa Pascal
Cấu trúc dữ liệu:
- Biểu diễn đồ thị G bằng cấu trúc dữ liệu bởi một ma trận trọng số A
nxn
.
Trong đó n là số đỉnh
- Sử dụng một ngăn xếp Stack[max] để lưu các đỉnh đã đi qua.
Tiểu luận nhóm 1 Trang 12
v
3
v
6

v
7
v
5
v
4
v
2
v
1
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
- Sử dụng một ngăn xếp CE[max] để lưu chu trình Euler tìm được.
Giải thuật :
Bước 1 : Kiểm tra đồ thị liên thông hay không
Hàm int lienthong() ;
Bước 2 : Kiểm tra dồ thị là đồ thị Euler hay không phải là đồ thị Euler (dựa
vào bậc của mỗi đỉnh).
Hàm int kiemtraEuler() ;
Bước 3 : Nếu đồ thị liên thông và không Euler thì ta sử dụng định lý
Goodman để chuyển đồ thị thành Euler.
Ta sử dụng các hàm sau đây :
void timdinhle() ; //Tìm các đỉnh lẻ của đồ thị ban đầu
void phanhoach() ; // Dùng để phân hoạch các đỉnh lẻ thành các cặp.
void Hoanvi ;//sinh ra các hoán vị phân hoạch và chọn phân hoạch tối
ưu.
void Themcanh() ; // Thêm cạnh vào đồ thị ban đầu để trở thành đồ thị
Euler.
Int duongdi(int s, int d) ; // tìm đường đi ngắn nhất giữa đỉnh s và t
Bước 4 : Từ đồ thị Euler ta đi tìm chu trình Euler
void Euler(dothi G) ; // tìm chu trình Euler

Thủ tục EULER (G: Matran) được thiết kế như sau.
Procedure EULER(G: Matran, CE: Nganxep);
Begin
v, x, top: Int //bien top: dinh stack
top=1;
stack[top]=u; // tim chu trinh bat dau tu dinh u
Do
Begin
V := stack[top]; // v la dinh dau stack
x:=1;
While (x<=n && A[v][x]==0) // tim dinh x ke v
x:=x+1;
if (x>n) // neu khong dinh x nao ke dinh v
Begin
//lay dinh v ra khoi stack va dua dinh v vao ngan xep CE
dCE :=dCE+1; //
CE[dCE] :=v;
Tiểu luận nhóm 1 Trang 13
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
top :=top-1;
End
Else // co dinh x ke dinh v
Begin
// dua dinh x vao stack dong thoi xoa canh (v,x) ra khoi do
thi.
top:=top+1;
stack[top]:=x;
A[v,x]:=A[x,v]:=0;
End
End while(top<>0); // Ham Euler thuc hien trong khi stack

chua rong.
End.
2.5. Bài toán người đưa thư
2.5.1. Phát biểu bài toán
Bài toán người đưa thư Trung Hoa (Chinese postman problem) phát biểu rằng:
Một người đưa thư xuất phát từ bưu điện phải đến một số con đường để
phát thư rồi quay trở về điểm xuất phát, hỏi người đó phải đi như thế nào để số
đường đi là ít nhất.
Trong phần đồ thị, bài toán người đưa thư Trung Hoa tương đương với
bài toán tìm chu trình ngắn nhất đi qua tất cả các cạnh của một đồ thị cho trước.
Tên gọi "bài toán người đưa thư Trung Hoa" được Alan Goldman của cục tiêu
chuẩn quốc gia Hoa Kỳ (U.S. National Bureau of Standards) đặt cho, vì nó được
nhà toán học Trung Hoa Mei-Ku Guan nêu ra đầu tiên vào năm 1962.
Bài toán giải bằng phương pháp đồ thị. Dựng một đồ thị có các cạnh
tương ứng với các con đường mà người đưa thư phải đi qua. Một chu trình đi qua
tất cả các cạnh gọi là một hành trình. Đỉnh xuất phát của chu trình này tương ứng
với vị trí của bưu điện. Nếu đồ thị là đồ thị Euler (các đỉnh đều có bậc chẵn) thì
sẽ tồn tại hành trình là chu trình đơn.
Ta xét trường hợp đồ thị có một số đỉnh bậc lẻ. Như thế hành trình của
người đưa thư sẽ đi qua một số cạnh hai lần.
2.5.2. Giải thuật
B1: Khởi tạo. Tính tập đỉnh bậc lẻ U, |U|=2k. Tính khoảng cách d(u,v) từng cặp
phần tử (u,v) của U, min=vô cùng
Tiểu luận nhóm 1 Trang 14
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
B2: Tìm phân hoạch s
min
có tổng khoảng cách min.
Với mỗi phân hoạch s gồm k cặp phần tử của U thực hiện.
{ Tính tổng T(s) = tổng d(u,v)

Nếu T(s)< min thì đặt min:=T(s) và s
min
:=s}
B3: Lập đồ thị G’=(V,E’), trong đó E’=E hợp s
min
. Bậc các đỉnh trong G’ có bậc
chẵn, suy ra đồ thị G’ có chu trình Euler. Tìm chu trình Euler G’ và C’.
B4: Thay mỗi cạnh (u,v) thuộc s
min
bằng đường đi min trong G, ta nhận được lộ
trình đưa thư ngắn nhất.
2.5.3. Giải thuật mịn
B1: Khởi tạo
//Nhập mảng hai chiều cấp n, với n là số đỉnh
Nhập n;
Nhập tên các đỉnh vào mảng Ten[i] \i=1,n
Nhập trị số cung A[Ten[i],Ten[j]] \i,j=1,n
//Tính tập đỉnh bậc lẻ
le:=0;
for mỗi thành phố i do
Begin
For mỗi thành phố j do
If cung(i,j)<>0 then
đếm bậc của đỉnh i
if (bậc của i là lẻ) then
begin tăng đỉnh bậc lẻ
thêm đỉnh lẻ vào mảng B
End;
End;
// Tính khoảng cách giữa các cặp đỉnh bac le

socap;=0; //biến số cặp
For i:= 1 to số đỉnh lẻ -1 do
For j:= i+1 to số đỉnh lẻ do
Begin socap:=socap+1;
//lưu các cặp đỉnh vào các cạnh dưới 2 mảng dau, cuoi
Dau[socap]:=B[i]; Cuoi[socap]:=B[j];
disjtra(A,B[i],B[j]);
D[socap]:=min;
// D la mang chua khoang cách ngắn nhất hai dinh
// thủ tục disjtra được xd phần sau.
End;
B2: Tìm phân hoạch Smin có tổng khoảng cách min
For mỗi cặp đỉnh (i,j) thuộc B do
Begin
danhdau[i]=danhdau[j]=False;//chưa đánh dấu
Cap[i,j]=False; //chưa đánh dấu
End;
For k:cặp đỉnh lẻ (i,j) thuộc B do
If(i,j chưa đánh dấu)and (cặp i,j chưa đánh dấu) then
Begin
Tính tổng khoảng cách của phân hoạch
Đếm số cặp DSC
Tiểu luận nhóm 1 Trang 15
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
//Lưu số thứ tự cặp đỉnh được chọn vào mảng C
C[DSC]=k;
If (số cặp đỉnh lẻ = đếm số cặp) then
If Tổng k/c<Smin then
Begin
Smin:=tổng k/c;

Lưu mảng C vào mảng M
//lưu M số thứ tự các cặp đỉnh thuộc phân hoạch nhỏ nhất
end
Cap[k]:=true;
Danhdau[i]=false; //với mọi dỉnh lẻ
End
Else danhdau[dau[k]]=danhdau[cuoi[k]]=True
B3: Lập đồ thị G’,Tìm chu trình Euler
// danh sách cạnh, thêm các cạnh ảo
for i:= 1 to n
for j:= i+1 to n do
begin t:=t+1;
if(phần tử của mảng A[Ten[i],Ten[j]] <>0)then
//lưu cặp đỉnh vào mảng dau1[t],cuoi1[t]
Dau1[t]=Ten[i] ;cuoi1[t]=Ten[j]
end;
For cặp đỉnh trong mảng M do //Mảng M chứa phân hoạch nhỏ nhất
begin
t:=t+1;
lưu vào hai mảng dau1,cuoi1
end;
/tìm chu trình Euler
c:=0; top:=0;
dau1[0]stack //đường đi bắt đầu từ đỉnh dau1[0]
while top<>0 do
begin
xstack
i:=0;
//t là số cạnh
while (i<=t-1) và (chưa gặp cạnh có đầu là x)do i:=i+1;

if cạnh có đầu là x then //tìm được đỉnh kề x
begin
stackcuoi1[i];
{loai bo canh x,y khỏi dau1,cuoi1}
while i<=n-2 do
begin
dau1[i]:=dau1[i+1];
cuoi1[i]:=cuoi1[i+1];
i:=i+1;
end;
t:=t-1;
end
else //không tìm thấy đỉnh kề nó
begin
c:=c+1;
CE[c]:=x;
Tiểu luận nhóm 1 Trang 16
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
end;
end;
end;
B4:// Chu trình euler được lưu vào mảng CE, thay các cạnh ảo bằng các đường đi
ngắn nhất
J:=1;
For mỗi cặp thứ i thuộc phân hoạch M do
Begin
x:=dau[M[i]]; y:=cuoi[M[i];
//c là số đỉnh trong chu trình Euler
While (j<=c) và (cặp đỉnh liền nhau j,j+1 ko thuộc
Smin) do

j:=j+1;
If cặp(j,j+1) thuộc Smin then
begin disjtra(A,x,y);
q=y; P1q;
while q<>x do
begin
q:=truoc[q];
P1q;//P1 là mảng đường đi từ y đến x
End;
// chia mang CE thanh hai mang con de them vao giua hai mang con
// phan duong di thay the duong di ao.
K:=0;
For q:=j+1 to c do // c la so dinh trong mang ce
Begin K:=k+1; ce1[k]:=ce[q] end;
// Dua stack vao sau doan ce bi cat
For h := dodaiP1 downto 1 do
Begin P1[h]ce end;
Nối ce1 vào sau ce;
End.



Tiểu luận nhóm 1 Trang 17
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
CHƯƠNG III. CÀI ĐẶT BÀI TOÁN NGƯỜI ĐƯA THƯ
3.1. Tìm đường đi, chu trình Euler
#include<conio.h>
#include<stdio.h>
#include<iostream.h>
const max= 20;

const vocung=32767;
struct dothi {
int n;
int A[max][max];
};
struct stack {
int top;
int A[max];
};
dothi G;
int chuaxet[max];
int m,i,j,sodinh, dodaimin=32767;
stack s,ce;
void khoitao();
void duyetdothi(int s);
int lienthong();
int kiemtraEuler();
void Euler(dothi G);
void main()
{
khoitao();
if (!lienthong()) {
printf("do thi khong lien thong");
}
else {
printf ("do thi lien thong");
if (kiemtraEuler()) {
printf(" va la do thi Euler");
Euler(G);
}

else
printf ("va khong la do thi Euler \n");
}
getchar();
}
void khoitao()
{
FILE *fp;
int i,j,k,d;
fp = fopen("E:\\BC\\tuyen\\GRAPH.INP","r");
if (fp == NULL)
printf("\nKhong the mo tap tin\n");
else {
fscanf(fp, "%d", &G.n); // so dinh cua do thi
fscanf(fp, "%d\n", &m); // so canh cua do thi
Tiểu luận nhóm 1 Trang 18
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
for (i=1;i<=G.n ;i++ )
for (j=1;j<=G.n ;j++ ) {
G.A[i][j]=0;
}
// doc vao ma tran trong so G.A[i][j]
for (k=1;k<=m ;k++ ) {
fscanf(fp, "%d", &i);
fscanf(fp, "%d", &j);
fscanf(fp, "%d", &d);
G.A[i][j]=d;
G.A[j][i]=d;
}
printf("\n so dinh cua do thi n= %d",G.n);

printf("\n so canh cua do thi m= %d",m);
printf("\n Ma tran trong so: \n");
for (i=1;i<=G.n ;i++ ){
for (j=1;j<=G.n ;j++ )
printf(" %d ",G.A[i][j]); printf("\n");
}
}
fclose(fp);
sodinh=G.n ;
}
void duyetdothi(int s)
{
int v;
for (v=1;v<=G.n ;v++ ){
if ((G.A[s][v]) && (chuaxet[v])) {
chuaxet[v]=0; duyetdothi(v);
}
}
}
int lienthong()
{
int i;
for (i=1;i<=G.n ;i++ )
chuaxet[i]=1;
chuaxet[1]=0; duyetdothi(1);
for (i=1;i<=G.n ;i++ )
if (chuaxet[i]) return 0;
return 1;
}
int kiemtraEuler()

{
int dem,ke,i,j;
dem=0; //dem so dinh bac le
for (i=1;i<=G.n ;i++ ) {
ke=0; //tim so dinh ke voi dinh i
for (j=1;j<=G.n ;j++ )
if (G.A[i][j]>0) ke=ke+1;
if (ke%2==1)
dem=dem+1;
}
Tiểu luận nhóm 1 Trang 19
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
if (dem!=0)
return 0; else
return 1;
}
void Euler(dothi G)
{
int x,u,v,d;
int W[max][max];
for (i=1;i<=G.n ;i++ )
for (j=1;j<=G.n ;j++ )
W[i][j]=G.A[i][j];
s.top=0; ce.top=0;
printf("\n Noi nguoi dua thu bat dau la: ");
scanf("%d",&u);
s.A[s.top]=u;
do{
v=s.A[s.top]; x=1;
while ((x<=G.n) && (!G.A[v][x])) x=x+1;

if (x>G.n){
ce.top=ce.top+1; ce.A[ce.top]=v;
s.top=s.top-1;
}
else {
s.top=s.top+1; s.A[s.top]=x;
G.A[v][x]=0; G.A[x][v]=0;
}
}while (s.top);
v=1;
while (v<ce.top) {
while (ce.A[v]<=sodinh)
v=v+1;
if (v<ce.top) {
for (x=v+1;x<=ce.top ;x++ )
ce.A[x-1]=ce.A[x]; ce.top=ce.top-1;
}
}
d=0;
for (u=1;u<ce.top ;u++ ) {
d= d + W[ce.A[u]][ce.A[u+1]]; printf(" \n d = %d", d);
}
d= d + W[ce.A[u]][ce.A[1]];
printf(" \n do dai = %d", d);
printf("\n Hanh trinh cua nguoi dua thu nhu sau: ") ;
for (u=1;u<=ce.top ;u++ ) {
printf("%d=>", ce.A[u]);
}
printf("%d",ce.A[1]);
getchar();

}
Minh họa chạy chương trình:
Đồ thị G:
Tiểu luận nhóm 1 Trang 20
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
File dữ liệu vào: GRAPH.INP
Kết quả khi chạy chương trình:
3.2. Giải bài toán người đưa thư
#include<conio.h>
#include<stdio.h>
#include<iostream.h>
const max= 20;
const vocung=500;
struct dothi {
int n;
int A[max][max];
};
struct dinhbacle {
Tiểu luận nhóm 1 Trang 21
1
2
3
4
5 6
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
int n; int A[max];
};
struct stack {
int top; int A[max];
};

typedef int mangphanhoach[max];
dothi G;
int chuaxet[max]; int W[max][max];
dinhbacle dinhle,H, vetdi;
mangphanhoach b,q;
int m, i,j,sodinh, dodaimin=500; stack s,ce;
void khoitao();
void duyetdothi(int s);
int lienthong();
int kiemtraEuler();
void timdinhle();
int duongdi(int s, int t);
void hoanvi(int k,int i,mangphanhoach B);
void phanhoach();
void themcanh(int i,int j);
void vetduongdi(int s, int t);
void suadothi();
void Euler(dothi G);
void main()
{
khoitao(); if (!lienthong())
{
printf("do thi khong lien thong");
}
Else {
printf ("do thi lien thong");
if (kiemtraEuler())
{
printf("va la do thi Euler");
} else

{
printf ("va khong la do thi Euler \n");
timdinhle(); phanhoach();
suadothi();
}
Euler(G);
}
getchar();
}
void khoitao()
{
FILE *fp; int i,j,k,d;
fp = fopen("E:\\BC\\tuyen\\GRAPH1.INP","r");
if (fp == NULL)
printf("\nKhong the mo tap tin\n");
else {
fscanf(fp, "%d", &G.n); fscanf(fp, "%d\n", &m);
for (i=1;i<=G.n ;i++ )
Tiểu luận nhóm 1 Trang 22
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
for (j=1;j<=G.n ;j++ ) {
G.A[i][j]=0;
}
for (k=1;k<=m ;k++ ) {
fscanf(fp, "%d", &i); fscanf(fp, "%d", &j);
fscanf(fp, "%d", &d);
G.A[i][j]=d; G.A[j][i]=d;
}
printf("\n So dinh cua do thi G: n= %d",G.n);
printf("\n So canh cua do thi G: m= %d",m);

printf("\n Ma tran trong so: \n");
for (i=1;i<=G.n ;i++ ){
for (j=1;j<=G.n ;j++ )
printf(" %d ",G.A[i][j]); printf("\n");
}
}
fclose(fp); sodinh=G.n ;
}
void duyetdothi(int s)
{
int v;
for (v=1;v<=G.n ;v++ ){
if ((G.A[s][v]) && (chuaxet[v])) {
chuaxet[v]=0;
duyetdothi(v);
}
}
}
int lienthong()
{
int i;
for (i=1;i<=G.n ;i++ )
chuaxet[i]=1;
chuaxet[1]=0;
duyetdothi(1);
for (i=1;i<=G.n ;i++ )
if (chuaxet[i])
return 0; return 1;
}
int kiemtraEuler()

{
int dem,ke,i,j; dem=0;
for (i=1;i<=G.n ;i++ ) {
ke=0;
for (j=1;j<=G.n ;j++ )
if (G.A[i][j]>0) ke=ke+1;
if (ke%2==1)
dem=dem+1;
}
if (dem!=0) return 0;
else return 1;}
void timdinhle()
{
Tiểu luận nhóm 1 Trang 23
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
int ke,i,j; H.n=0;
printf(" Dinh le: ");
for (i=1;i<=G.n ;i++ ){
ke=0; for (j=1;j<=G.n ;j++)
if (G.A[i][j]>0)
ke=ke+ 1;
if (ke%2==1){
H.n=H.n+1; H.A[H.n]=i;
}
}
for (i=1;i<=H.n ;i++ )
printf(" %d ",H.A[i]);
}
int duongdi(int s, int t)
{

int min,u,i; int L[max];
int W[max][max];
for (i=1;i<=G.n ;i++ )
for (j=1;j<=G.n ;j++ )
if (G.A[i][j]>0)
W[i][j]=G.A[i][j];
else
W[i][j]=vocung;
for (i=1;i<=G.n ;i++ ){
L[i]=vocung; chuaxet[i]=1;
}
L[s]=0;
while (chuaxet[t]){
min=vocung;
for (i=1;i<=G.n ;i++ ){
if ((chuaxet[i]==1) && (min>L[i])) {
min=L[i]; u=i;
}
}
chuaxet[u]=0; for (i=1;i<=G.n ;i++ )
if ((chuaxet[i]==1) && (L[i]>L[u]+W[u][i]))
L[i]=L[u]+W[u][i];
}
// printf("duong di %d ",L[t]);
return L[t];
}
void hoanvi(int k,int i,mangphanhoach B)
{
int j,dodai,u;
if (k==H.n){

dodai=0; u=1;
while (u<=H.n){
dodai=dodai+ duongdi(B[u],B[u+1]);
u=u+2;
}
if (dodai<dodaimin){
for (i=0;i<=max;i++) q[i]=B[i];
dodaimin=dodai;
Tiểu luận nhóm 1 Trang 24
Toán ứng dụng Chu trình Euler và bài toán người đưa thư
}
}
else {
//printf(" hoanvi k<>h");
for (j=1;j<=H.n ;j++ )
if (chuaxet[j]){
k=k+1; chuaxet[j]=0;
B[k]=H.A[j]; hoanvi(k,j,B);
k=k-1; chuaxet[j]=1;
}
}
}
void phanhoach()
{
int i,k;
dodaimin=500;
for (i=1;i<=H.n ;i++ ) chuaxet[i]=1;
for (i=1;i<=H.n ;i++ ){
k=1; chuaxet[i]=0;
b[k]=H.A[i]; hoanvi(k,i,b);

chuaxet[i]=1;
}
}
void themcanh(int i,int j)
{
int k;
G.n=G.n+1;
for (k=1;k<=G.n ;k++ )
G.A[k][G.n]=0;
for (k=1;k<=G.n ;k++ )
G.A[G.n][k]=0; G.A[i][G.n]=1;
G.A[G.n][i]=1; G.A[j][G.n]=1; G.A[G.n][j]=1;
printf("\n Them dinh %d va them canh %d,%d va canh %d,
%d",G.n,i,G.n,G.n,j);
}
void vetduongdi(int s, int t)
{
int min,u,i;
int L[max];
int p[max];
int W[max][max];
for (i=1;i<=G.n ;i++ )
for (j=1;j<=G.n ;j++ )
if (G.A[i][j]>0)
W[i][j]=G.A[i][j];
else
W[i][j]=vocung;
for (i=1;i<=G.n ;i++ ) {
L[i]=vocung;
chuaxet[i]=1; p[i]=s;

}
Tiểu luận nhóm 1 Trang 25

×