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

đường đi hamilton và ứng dụng

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 (335.36 KB, 21 trang )

MỤC LỤC
LỜi mở đầu
2
Nội dung 3
1. Đồ thị Hamilton 3
1.1 Khái niệm đường đi, chu trình Hamilton 3
1.2 Các định lí 3
2. Thuật toán nhánh cận giải bài toán người du lịch 7
2.1 Thuật toán nhánh cận 7
2.2 Bài toán Người du lịch 7
2.3 Cách giải 7
2.4 Mô hình kĩ thuật nhánh cận 10
3. Ứng dụng của bài toán người du lịch 12
4. Cài đặt chương trình và minh họa kết quả 13
4.1 Cấu trúc dữ liệu 13
4.2 Cài đặt chương trình 15
4.3 Minh họa kết quả 18
Kết luận 20
Tài liệu tham khảo 21
LỜI MỞ ĐẦU
Lý thuyết đồ thị là một ngành Toán học có vị trí đặc biệt quan trọng về mặt lý
thuyết cũng như ứng dụng. Có nhiều bài toán liên quan tới một tập các đối tượng và
những mối liên hệ giữa chúng, đòi hỏi toán học phải đặt ra một mô hình biểu diễn một
cách chặt chẽ và tổng quát bằng ngôn ngữ ký hiệu, đó là đồ thị. Những ý tưởng cơ bản
của nó được đưa ra từ thế kỷ thứ XVIII bởi nhà toán học Thụy Sỹ Leonhard Euler, ông đã
dùng mô hình đồ thị để giải bài toán về những cây cầu Konigsberg nổi tiếng. Các bài toán
về đường đi, trong đó đồ thị Hamilton là nội dung quan trọng của Lý thuyết đồ thị.
Bài toán “Người du lịch” cũng là một vấn đề thú vị cho các nhà kinh doanh cũng
như ứng dụng vào đời sống. Sử dụng lý thuyết đồ thị để giải bài toán người du lịch không
chỉ là toán học thuần túy mà còn mở ra cho chúng ta nhiều phạm trù ứng dụng phong phú.
Mối liên hệ giữa bài toán “Người du lịch” với đồ thị Hamilton và những ứng dụng của nó


là một vấn đề mở, thu hút bất kỳ ai quan tâm đến nội dung này. Vì vậy, nhóm chúng tôi
chọn đề tài: “Đường đi Hamilton và ứng dụng” để nghiên cứu.
Mặc dù các thành viên trong nhóm đã có nhiều cố gắng nhưng trong quá trình thực
hiện bài tiểu luận sẽ có nhiều thiếu sót, nhóm chúng tôi rất mong nhận được sự góp ý của
thầy và các bạn.
Chúng tôi xin chân thành cảm ơn sự giúp đỡ nhiệt tình của PGS. TSKH Trần Quốc
Chiến trong môn học vừa qua.
Nhóm chúng tôi gồm 4 thành viên:
STT Họ và tên Nội dung thực hiện
1 Trần Duy Hà (trưởng nhóm) Ứng dụng, tổng hợp và hoàn thiện
2 Lê Thị Thanh Lam Đại cương về đồ thị Hamilton và thuật toán
3 Nguyễn Thị Cẩm Hường Thiết kế cấu trúc dữ liệu và thuật toán
4 Nguyễn Thị Hoài Thanh Cài đặt chương trình thuật toán
NỘI DUNG ĐỀ TÀI
1. Đồ thị Hamilton
1.1 Khái niệm đường đi, chu trình Hamilton
Cho đồ thị G = (V, E) có n đỉnh.
a. Chu trình (x
1
, x
2
, , x
n
, x
1
) được gọi là chu trình Hamilton nếu x
i
≠ x
j
với 1 ≤ i < j ≤ n

b. Đường đi (x
1
, x
2
, , x
n
) được gọi là đường đi Hamilton nếu x
i
≠ x
j
với 1 ≤ i < j ≤ n
Chúng ta có thể phát biểu lại như sau:
+ Chu trình Hamilton là chu trình xuất phát từ 1 đỉnh, đi thăm tất cả những đỉnh
còn lại mỗi đỉnh đúng một lần, cuối cùng quay trở lại với đỉnh xuất phát.
+ Đường đi Hamilton là đường đi qua tất cả các đỉnh của đồ thị, mỗi đỉnh đúng
một lần.
Khác với khái niệm chu trình Euler và đường đi Euler, một chu trình Hamilton không
phải là đường đi Hamilton bởi có đỉnh xuất phát được thăm tới 2 lần.
Ví dụ: Xét 3 đơn đồ thị G
1
, G
2
, G
3
sau:
Đồ thị G
1
có chu trình Hamilton (a, b, c, d, e, a). G
2
không có chu trình

Hamilton vì deg(a) = 1 nhưng có đường đi Hamilton (a, b, c, d). G
3
không có cả chu
trình Hamilton lẫn đường đi Hamilton.
1.2Các định lí cơ bản:
1.2.1 Điều kiện cần
* Định lí 1:
Giả sử đồ thị G có chu trình Hamilton C. Khi đó:
i) Đồ thị G liên thông.
ii) Mọi đỉnh của G lớn hơn hoặc bằng 2 và có đúng hai cạnh liên thuộc thuộc chu trình
C.
iii) Nếu xóa đi k đỉnh bất kỳ cùng các cạnh liên thuộc chúng thì đồ thị còn lại sẽ có tối
đa k thành phần liên thông.
* Hệ quả:
Giả sử đồ thị n đỉnh G có đường đi Hamilton P. Khi đó:
(i) Đồ thị G liên thông.
(ii) Có ít nhất n – 2 đỉnh bậc ≥ 2 và mỗi đỉnh có đúng hai cạnh liên thuộc thuộc đường
đi P.
(iii) Nếu xóa đi k đỉnh bất kỳ cùng các cạnh liên thuộc chúng thì đồ thị còn lại sẽ có
tối đa k + 1 thành phần liên thông.
1.2.2 Điều kiện đủ
* Định lí 1: Đồ thị đủ K
n
với n lẻ n ≥3 có n – ½ chu trình Hamilton từng đôi một
không giao nhau.
* Định lý 2 ( Dirac) : Cho G là đơn đồ thị n đỉnh (n ≥ 3). Nếu bậc deg(v) ≥ n/2 với
mọi đỉnh v của G thì G có chu trình Hamilton.
* Định lý 3: Cho G là đồ thị đơn n đỉnh (n ≥ 3). Nếu bậc d(v) ≥ (n-1)/2 với mọi đỉnh v
của G thì G có đường đi Hamilton.
* Định lý 4 : Cho G là đồ thị đơn n đỉnh (n≥3). Giả sử u và v là 2 đỉnh không kề nhau

của G sao cho deg(u) + deg(v) ≥ n
Khi đó G có chu trình Hamilton khi và chỉ khi đồ thị G + (u,v) có chu trình Hamilton.
* Định lý 5 : Cho G là đồ thị đơn giản n đỉnh. Giả sử G’ và G’’ là 2 đồ thị thu được từ
G bằng cách qui nạp nối tất cả các cặp đỉnh không kề nhau có tổng các bậc ít nhất
bằng n. Khi đó ta có G’ = G’’.
Từ định nghĩa trên ta có thể định nghĩa khái niệm bao đóng của đồ thị .
* Bao đóng : C(G) của đồ thị G n đỉnh là đồ thị thu được từ G bằng cách, theo quy
nạp, nối tất cả các cặp đỉnh không kề nhau mà tổng số bậc ít nhất bằng n cho đến khi
không còn cặp đỉnh nào như vậy nữa.
* Định lý 6: Đồ thị G có chu trình Hamilton khi và chỉ khi bao đóng của G có chu
trình Hamilton.
* Định lý 7: Nếu bao đóng C(G) = K
n
(n

3) thì đồ thị G có chu trình Hamilton.
* Định lý 8 ( Định lý Ore): Cho G là đơn đồ thị n đỉnh (n

3).
Nếu deg(u) + deg(v)

n với mọi cặp đỉnh không kề nhau thì đồ thị G có chu trình
Hamilton.
* Định lý 9 : Cho G là đơn đồ thị n đỉnh (n

3) và m cạnh. Nếu m

C(n-1,2) + 2 thì
đồ thị G có chu trình Hamilton.
* Định lý 10: Cho đồ thị G là đồ thị lưỡng phân với 2 tập đỉnh V

1
và V
2
sao cho
card(v
1
) = card(v
2
) = n ≥ 2
Nếu deg(v) > n/2 với mọi đỉnh v của G thì G có chu trình Hamilton.
* Định lý 11: Đồ thị K
m,n
có chu trình Hamilton khi và chỉ khi m = n.
* Định lý 12: Đồ thị K
m,n
có đường đi Hamilton khi và chỉ khi
1m n
− =
.
1.2.1 Đồ thị có hướng:
* Định lý 13: (Điều kiện đủ tồn tại chu trình có hướng Hamilton)
a. (Meyniel) Nếu đồ thị G liên thông mạnh và
deg(u) + deg(v) ≥ 2n - 1,

u, v

G không kề nhau
thì G có chu trình có hướng Hamilton
b. (Ghoula – Houri) Nếu đồ thị G liên thông mạnh và
deg(v)


n

v

G
thì G có chu trình có hướng Hamilton.
c. (Woodall) Nếu
deg
0
(u) + deg
1
(v)

n

u, v

G không tồn tại cung từ u từ v
thì G có chu trình có hướng Hamilton
d. Nếu
deg
1
(v)

n/2 & deg
0
(v)

n/2


v

G
thì G có chu trình có hướng Hamilton.
* Định lý 14: (Điều kiện đủ tồn tại đường đi có hướng Hamilton)
a. Nếu
deg(u) + deg(v)

2n - 3

u, v

G không kề nhau
thì G có đường đi có hướng Hamilton.
b. Nếu
deg(v)

n - 1

v

G
thì G có đường đi có hướng Hamilton.
c. Nếu
deg
0
(u) + deg
1
(v)


n – 1

u, v

G không tồn tại cung từ u đến v
thì G có đường đi có hướng Hamilton.
d. Nếu
deg
1
(v)

n/2 & deg
0
(v)

n/2

v

G
thì G có đường đi có hướng Hamilton.
Bây giờ chúng ta nghiên cứu đường đi và chu trình có hướng Hamilton trong đồ thị có
hướng đủ (có đồ thị lót đủ). Trước hết là định lý khẳng định sự tồn tại đường đi có
hướng Hamilton trong đồ thị có hướng đủ.
* Định lý 15: (Konig) Mọi đồ thị có hướng đủ đều có đường đi có hướng Hamilton.
* Định lý 16: Đồ thị có hướng đủ G = (V, E) gọi là bắc cầu nếu (u, v) và (v, w)

E
suy ra (u, w)


E.
Từ định nghĩa ta thấy ngay, một đồ thị có hướng đủ là bắc cầu khi và chỉ khi nó không
có chu trình có hướng độ dài 3.
* Định lý 17: Đồ thị có hướng đủ bắc cầu khi và chỉ khi nó không có chu trình có
hướng.
* Định lý 18: Đường đi Hamilton trong đồ thị có hướng đủ là duy nhất khi và chỉ khi
đồ thị bắc cầu.
* Định lý 19: (Moon – Moser) Cho G = (V, E) là đồ thị có hướng đủ liên thông mạnh
bậc n (n

3). Khi đó với mọi đỉnh v và số nguyên p (3

p

n) luôn tồn tại chu trình
có hướng sơ cấp độ dài p qua đỉnh v.
* Định lý 20: (Camion) Đồ thị có hướng đủ có chu trình có hường Hamilton khi và
chỉ khi nó liên thông mạnh.
2. Thuật toán nhánh cận giải bài toán người du lịch
2.1 Thuật toán nhánh cận:
Thuật toán nhánh cận là một trong các phương pháp chủ yếu giải bài toán tối ưu tổ
hợp. Tư tưởng cơ bản của nó là trong quá trình tìm kiếm ta phân hoạch các phương án
của bài toán ra thành hai hay nhiều tập con như là các nút của cây tìm kiếm và cố gắng
đánh giá cận cho các nút, loại bỏ những nhánh mà ta biết chắc chắn là không chứa
phương án tối ưu.
2.2 Bài toán Người du lịch:
Một người xuất phát từ một thành phố nào đó muốn tới thăm n−1 thành phố khác,
mỗi thành phố đúng một lần, rồi quay về thành phố ban đầu. Hỏi nên đi theo trình tự nào
để độ dài tổng cộng các đoạn đường đi qua là ngắn nhất (khoảng cách giữa hai thành phố

có thể hiểu là cự li thông thường hoặc thời gian cần đi hoặc chi phí của hành trình, và
xem như cho trước).
Gọi C = { c
ij
: i,j = 1,2, ,n} là ma trận chi phí.
Mỗi hành trình: v = v(1)→v(2)→ →v(n-1)→v(n)→v(1) có thể viết dưới dạng:
v = (v(1),v(2)), (v(2),v(3)), , (v(n-1),v(n)), (v(n),v(1))
Trong đó, mỗi thành phần (v(i-1),v(i)) gọi là một cạnh của hành trình.
Xét đồ thị đầy đủ G = (V,E), với V = {1, 2, , n}, có trọng số với trọng số c
ij
=
c(i,j) có thể khác c
ji
= c(j,i). Như vậy, ta có thể xem G như là một đồ thị có hướng đầy đủ
“mạnh” theo nghĩa với mọi i, j=1, 2, , n, i≠j, luôn có (i,j), (j,i)∈E. Bài toán trở thành
tìm chu trình Hamilton có độ dài ngắn nhất trong G.
2.3 Cách giải
Xét bài toán người du lịch:
Gọi C = { c
ij
: i,j = 1,2, ,n} là ma trận chi phí.
Mỗi hành trình: v = v(1)→v(2)→ →v(n-1)→v(n)→v(1) có thể viết dưới dạng:
v = (v(1),v(2)), (v(2),v(3)), , (v(n-1),v(n)), (v(n),v(1))
Trong đó, mỗi thành phần (v(i-1),v(i)) gọi là một cạnh của hành trình.
Trong bài toán người du lịch khi tiến hành tìm kiếm lời giải ta sẽ phân tập hành
trình thành hai tập con: Một tập chứa cạnh (i,j) và tập không chứa cạnh này. Ta gọi việc
đó là phân nhánh, mỗi tập con nói trên gọi là nhánh. Việc phân nhánh được minh họa bởi
cây tìm kiếm:
Việc phân nhánh sẽ được dựa trên qui tắc hợp lý nào đó cho phép rút ngắn quá
trình tìm kiếm phương án tối ưu. Sau khi phân nhánh sẽ tính cận dưới của hàm mục tiêu

trong mỗi tập con nói trên. Việc tìm kiếm sẽ tìm trên tập con có cận dưới nhỏ hơn. Thủ
tục sẽ tiếp tục cho đến khi thu được hành trình đầy đủ, tức là phương án của bài toán
người du lịch. Sau đó chỉ cần xét những tập con có cận dưới nhỏ hơn giá trị hàm mục tiêu
tìm được. Kỹ thuật tính cận dưới dựa trên thủ tục rút gọn dưới đây.
2.3.1 Thủ tục rút gọn
Tổng chi phí của một hành trình sẽ chứa đúng một phần tử trên mỗi dòng và mỗi
cột của ma trận chi phí C = (c
ij
). Do đó nếu trừ bớt mỗi phần tử của một dòng (hay một
cột) đi cùng một giá trị thì chi phí của tất cả hành trình sẽ giảm đi một lượng, vì thế hành
trình tối ưu sẽ không thay đổi. Vì vậy, nếu tiến hành trừ bớt các phần tử của mỗi dòng và
mỗi cột đi một hằng số sao cho thu được ma trận không âm và mỗi cột cũng như mỗi
dòng chứa ít nhất một số 0, thì tổng các hằng số trừ đi đó sẽ cho ta cận dưới của mọi hành
trình. Thủ tục trừ bớt này gọi là thủ tục rút gọn, các hằng số trừ ở mỗi dòng (cột) gọi là
hằng số rút gọn dòng (cột), ma trận thu được gọi là ma trận rút gọn.
Thủ tục rút gọn:
+ Đầu vào: Ma trận chi phí C = (c
ij
)
+ Đầu ra: Ma trận rút gọn và tổng hằng số rút gọn Sum
+ Thuật toán:
(i) Khởi tạo :
Sum := 0 ;
(ii) Rút gọn dòng :
Với mỗi dòng r từ 1 đến n của ma trận C thực hiện :
- Tìm phần tử c
rj
= α nhỏ nhất trên dòng.
- Trừ tất cả các phần tử trên dòng đi một lượng α.
- Cộng dồn : Sum := Sum + α

(iii) Rút gọn cột :
Với mỗi cột c từ 1 đến n của ma trận C thực hiện :
- Tìm phần tử c
ic
= α nhỏ nhất trên cột.
- Trừ tất cả các phần tử trên cột đi một lượng α.
- Cộng dồn : Sum := Sum + α
2.3.2 Thủ tục phân nhánh
Giả sử ta chọn cạnh phân nhánh (r, s). Như vậy các hành trình sẽ được chia làm
hai tập: P
1
chứa các hành trình qua (r, s) và P
2
chứa các hành trình không qua (r, s).
+ Nhánh tập P
1
: Cận dưới β với giá trị xuất phát có từ thủ tục rút gọn.
- Giảm cấp ma trận chi phí C bằng cách loại dòng r và cột s.
- Ngăn cấm tạo hành trình con :
Cấm cạnh (s,r) bằng cách đặt c
sr
= ∞.
Nếu (r,s) là cạnh phân nhánh thứ hai trở đi thì phải xét các cạnh đã chọn nối trước và
sau cạnh (r,s) thành dãy nối tiếp các cạnh như hình sau:
→ → →
(i,j) (r,s) (k,h)
và cấm tất cả các cạnh dạng (h,i) bằng cách đặt c
hi
= ∞.
Rút gọn ma trận chi phí ta có cận dưới β := β + (tổng hằng số rút gọn).

Ta có thể tiếp tục thủ tục phân nhánh theo nhánh này với ma trận chi phí đã được hiệu
chỉnh và giảm 1 bậc. Việc chọn cạnh nào để phân nhánh ta sẽ bàn ở mục tiếp theo.
+ Nhánh tập P
2
:
- Cấm cạnh (r,s) bằng cách đặt c
rs
= ∞.
- Thực hiện thủ tục rút gọn với ma trận chi phí tương ứng và tính cận dưới
β := β + (tổng hằng số rút gọn) cho nhánh.
Ta có thể tiếp tục thủ tục phân nhánh theo nhánh này với ma trận chi phí đã được hiệu
chỉnh cùng cận dưới tương ứng.
2.3.3 Thủ tục chọn cạnh phân nhánh
Một cách lôgic là ta chọn cạnh phân nhánh (r,s) sao cho cận dưới của nhánh không chứa
(r,s) sẽ tăng nhiều nhất.
Thủ tục chọn cạnh phân nhánh (r,s):
+ Đầu vào : Ma trận rút gọn bậc k
+ Đầu ra : Cạnh phân nhánh (r,s).
+ Thuật toán :
(i) Khởi tạo : α := - ∞;
(ii) Với mỗi cặp (i,j) thoả c
ij
= 0 (i=1, ,k; j=1, ,k) thực hiện
- Xác định: minr = min{c
ih
: h ≠ j }
mins = min{c
hj
: h ≠ i }
- Nếu α < minr + mins, đặt: α:= minr + mins;

r:= i;
s := j;
2.3.4 Chọn hai cạnh cuối cùng
Mỗi hành trình có n cạnh. Sau khi đã chọn n-2 cạnh, ta phải chọn nốt 2 cạnh còn lại.
Lúc này ma trận rút gọn có bậc 2 và là một trong hai dạng sau:
u v u v
p 0 ∞ p ∞ 0
q ∞ 0 q 0 ∞
(i) (ii)
cùng cận dưới β.
Trong trường hợp (i) ta chọn hai cạnh (p,u) và (q,v), còn trong trường hợp (ii) ta chọn
hai cạnh (p,v) và (q,u). Tổng chi phí là β.
2.4 Mô hình kĩ thuật nhánh cận
 Hàm này khởi tạo giá trị ban đầu
void Initial()
{
<Khởi tạo một cấu hình bất kỳ BESTCONFIG>;
}
 Hàm này thử chọn cho x[i] tất cả các giá trị nó có thể nhận
void Try(int i)
{
for <Mọi giá trị V có thể gán cho x[i]> do
{
<Thử cho x[i] = V>;
if <Việc thử trên vẫn còn hi vọng tìm ra cấu hình tốt hơn BESTCONFIG>
then
if <x[i] là phần tử cuối cùng trong cấu hình> then
<Cập nhật BESTCONFIG>
else
{

<Ghi nhận việc thử x[i] = V nếu cần>;
Try(i + 1); {Gọi đệ quy, chọn tiếp x[i+1]}
<Bỏ ghi nhận việc thử cho x[i] = V (nếu cần)>;
}
end;
end;
 Chương trình chính
int main() {
Initial();
Try(1);
<Thông báo cấu hình tối ưu BESTCONFIG>;
}
Kỹ thuật nhánh cận thêm vào cho thuật toán quay lui khả năng đánh giá theo từng
bước, nếu tại bước I, giá trị thử gán cho xi không có hy vọng tìm thấy cấu hình tốt hơn
cấu hình BESTCONFIG thì thử giá trị khác ngay mà không cần phải gọi đệ quy tìm tiếp
hay nhận kết quả làm gì. Nghiệm của bài toán sẽ được làm tốt dần, bởi khi tìm ra một cấu
hình mới (tốt hơn BESTCONFIG – tất nhiên), ta không in kết quả ngay mà sẽ cập nhật
BESTCONFIG bằng cấu hình mới vừa tìm được.
3. Ứng dụng của bài toán Người du lịch
Bài toán Người du lịch chỉ là mô hình tiêu biểu của ứng dụng chu trình Hamilton. Từ mô
hình này, ta sẽ thấy rất nhiều ứng dụng trong thực tế. Tuy nhiên trong đề tài này, chúng tôi
chỉ nêu một vài ứng dụng tiêu biểu đó là bài toán kinh tế và quản lý dữ liệu.
 Bài toán kinh tế:
Giả sử một công ty A muốn mở rộng các đại lý tiêu thụ sản phẩm. Vị trí các cửa hàng như
hình vẽ. Để vận chuyển hàng hóa từ kho (vị trí 1) đến các cửa hàng (các vị trí còn lại),
người quản lý phải tính toán sao cho quá trình vận chuyển qua mỗi cửa hàng chỉ đi đúng
một lần với chi phí thấp nhất hoặc quãng đường đi ngắn nhất. Để giải quyết vấn đề này,
rõ ràng phải sử dụng giải thuật của bài toán Người du lịch đã nêu trên.
 Quản lý dữ liệu
Giải sử có một mạng máy tính nội bộ (của một cơ quan, tổ chức nào đó…) được bố trí

như hình vẽ.
Khi đó, cần phải tính toán sao cho việc lắp đặt đường dây từ vị trí 1 để kết nối với các vị
trí khác sao cho mỗi vị trí, đường dây đi qua đúng 1 lần và đảm bảo chi phí lắp đặt là ít
nhất hoặc chiều dài dây cáp lắp đặt là ngắn nhất.
Qua hai ví dụ trên, ta có thể thấy những ứng dụng của Đồ thị Hamilton và bài toán Người
du lịch là rất phong phú và phổ biến trong cuộc sống hàng ngày. Chẳng hạn ví dụ của bài
toán trên có thể mở rộng cho bất kỳ lĩnh vực khác như:
- Xe cứu thương ở vị trí 1, bệnh nhân ở vị trí 3. Vậy phải tìm đường đi từ 1 đến 3
rồi trở về 1 như thế nào để đảm bảo thời gian đi là ít nhất.
- Nhà máy nước của một thành phố đặt ở vị trí 1, các hộ gia đình ở các vị trí còn
lại. Vậy phải lắp đặt hệ thống ống nước tới các hộ gia đình sao cho chi phí bỏ
ra là thấp nhất.
4. Cài đặt chương trình và minh họa kết quả
4.1 Cấu trúc dữ liệu và giải thuật
4.1.1 Đầu vào, đầu ra
Input: file văn bản TRAVEL.INP
• Dòng 1: Chứa số thành phố n (1 ≤ n ≤ 100) và số tuyến đường m trong mạng lưới
giao thông
• m dòng tiếp theo, mỗi dòng ghi số hiệu hai thành phố có đường đi trực tiếp và chi
phí đi trên quãng đường đó (chi phí này là số nguyên dương ≤ 10000)
Output: file văn bản TRAVEL.OUT, ghi hành trình tìm được.
4.1.2 Cấu trúc dữ liệu
Sử dụng ma trận C[size][size] để biểu diễn ma trận chi phí.
Sử dụng ma trận X[size+1] để thử các khả năng, ma trận bestWay[size+1] để ghi nhận
nghiệm.
Sử dụng ma trận T[size+1] để lưu chi phí từ X1 đến Xi.
Sử dụng ma trận Free[size] để đánh dấu, Freei=TRUE nếu chưa đi qua thành phần i.
Biến N, M tương ứng số đỉnh (thành phố) sẽ đi qua, và số cạnh (đường đi) giữa các đỉnh.
Biến minSpending lưu giữ chi phí hành trình tối ưu.
4.1.3 Giải thuật

• Bước 1: Kiểm tra file dữ liệu đầu “TRAVEL.INP” vào nếu phù hợp chuyển sang
bước 2, ngược lại đưa ra màn hình thông báo lỗi.
• Bước 2: Khởi tạo giá trị đầu (hàm khoiTao()),
Free[i] = TRUE, với i = 1 > n
Gán Free[1] = FALSE, các thành phố chưa đi qua, ngoại trừ thành phố 1
X[1] = 1, xuất phát từ thành phố 1
T[1] = 0, chi phí tại thành phố xuất phát là 0
minSpending = maxC, chi phí hành trình là lớn nhất (+∞)
• Bước 3: Thử các cách chọn Xi (hàm backTrack(int i), sử dụng giải thuật quay lui
để tìm thành phố có minSpending nhỏ nhất). Hàm được thiết kế như sau:
//Thử với các cách chọn X[i]
void backTrack(int i) {
//Thử các thành phố từ 2 đến N
for (int j=2; j<=N; j++) {
//Nếu gặp thành phố chưa đi qua, FREE[j]=0
if (FREE[j]) {
//Thử đi
X[i] = j;
//Chi phí = Chi phí bước trước + Chi phí đường đi trực tiếp
T[i] = T[i-1] + C[X[i-1]][j];
//Hiển nhiên, nếu có điều này thì C[X[i-1]][j] < +∞ rồi
if (T[i] < minSpending) {
//Nếu chưa đến được X[N]
if (i < N) {
//Đánh dấu thành phố vừa thử, FREE[j] = 0
FREE[j] = 0;
//Tìm các khả năng chọn X[i+1]
backTrack(i+1);
//Bỏ đánh dấu
FREE[j] = 1;

}
//Từ X[N] quay lại 1 vẫn tốn chi phí ít hơn trước
else if (T[N] + C[X[N]][1] < minSpending){
//Cập nhật BestConfig
for (int var=1; var<=N; var++)
bestWay[var] = X[var];
minSpending = T[N] + C[X[N]][1];
} } } } }
• Bước 4: Xuất kết quả, sử dụng hàm xuatKQ()
Kết quả chương trình được lưu trong file “TRAVEL.OUT”.
4.2 Cài đặt chương trình
Chương trình được cài đặt bằng ngôn ngữ lập trình C++.
#include <iostream.h>
#include <fstream.h>
#include <conio.h>
#include <stdio.h>
const int size = 20;
int maxE = 20;
int maxC = size * maxE;
int C[size][size];
int X[size+1];
int T[size];
int bestWay[size+1];
int FREE[size];
int minSpending;
int N, M;
char* FI = "TRAVEL.INP";
//char* FI = "TRAVEL1.INP";
char* FO = "TRAVEL.OUT";
fstream F;

void docFile() {
int i, j;
F.open(FI, ios::in);
F >> N >> M;
for (i=1; i<=N; i++)
for (j=1; j<=N; j++) {
if (i == j) C[i][j] = 0;
else C[i][j] = maxC;
}
do {
for (int k=1; k<=M; k++) {
F >> i >> j;
F >> C[i][j];
C[j][i] = C[i][j];
}
if (F.eof()) break;
} while (true);
F.close();
}
void khoiTao() {
for (int i=1; i<=N; i++)
FREE[i] = 1;
FREE[1] = 0;
X[1] = 1;
T[1] = 0;
minSpending = maxC;
}
void xuatKQ() {
F.open(FO, ios::out);
if (minSpending == maxC)

F << "NO HAMILTON CYCLE!!!" << endl;
else {
F << "HAMILTON CYCLE IS: ";
for (int i=1; i<=N; i++)
F << bestWay[i] << " >";
F << "1" << endl;
F << "MINIMUM SPENDING IS: " << minSpending << endl;
}
F.close();
}
void backTrack(int i) {
for (int j=2; j<=N; j++) {
if (FREE[j]) {
X[i] = j;
T[i] = T[i-1] + C[X[i-1]][j];
if (T[i] < minSpending) {
if (i < N) {
FREE[j] = 0;
backTrack(i+1);
FREE[j] = 1;
}
else if (T[N] + C[X[N]][1] < minSpending){
for (int var=1; var<=N; var++)
bestWay[var] = X[var];
minSpending = T[N] + C[X[N]][1];
}
}
}
}
}

int main() {
docFile();
khoiTao();
backTrack(2);
xuatKQ();
return 0;
}
4.3 Minh họa kết quả
4.3.1 Dữ liệu đầu vào
Đồ thị G, minh họa các thành phố và chi phí tương ứng giữa các thành phố tương
ứng.

Dữ liệu đầu vào được lưu trong file “TRAVEL.INP”

Dữ liệu đầu vào với file dữ liệu “TRAVEL.INP”
2
3
4
1
3
1
4
1
2
Dữ liệu đầu vào với file dữ liệu “TRAVEL1.INP”
4.1.4 Kết quả đầu ra
Kết quả chương trình được lưu ở file “TRAVEL.OUT”.

Kết quả đầu ra của dữ liệu “TRAVEL.INP” lưu trên file “TRAVEL.OUT”
Kết quả đầu ra của dữ liệu “TRAVEL1.INP” lưu trên file “TRAVEL.OUT

KẾT LUẬN
Qua nghiên cứu về chu trình Hamilton và những ứng dụng của nó thông qua bài
toán Người du lịch, nhóm chúng tôi chỉ đề cập được một phần nhỏ trong các ứng dụng
của nó. Các vị dụ trong đề tài, chúng tôi chỉ nêu ở dạng mô hình đơn giản, số đỉnh đưa ra
còn rất nhỏ. Qua tìm hiểu, người ta bắt đầu thử và công bố các kết quả giải bài toán này
trên máy tính với số đỉnh lớn hơn rất nhiều. Từ năm 1954 (49 đỉnh), cho đến năm 2004
bài toán giải được với số đỉnh lên tới 24.978, và dự báo sẽ còn tiếp tục tăng cao nữa.
Đây là một bài toán khó trong tin học, với độ phức tạp thuật toán cao, đòi hỏi cần
nhiều thời gian nghiên cứu, do đó trong khuôn khổ của đề tài này, nhóm chúng tôi chỉ tổ
chức xây dựng giải thuật và những ứng dụng của mô hình này trên thực tế. Việc chạy
chương trình ở cuối đề tài chỉ mang tính minh họa cho bài toán Người du lịch được viết
bằng ngôn ngữ lập trình C++.
Mặc dù đã có nhiều cố gắng nhưng kết quả của đề tài còn mang tính tương đối, tuy
vậy đề tài cũng đáp ứng được yêu cầu đề ra ban đầu. Dựa trên cơ sở nhóm đã tìm hiểu và
thực hiện chương trình này có thể được mở rộng cho việc kiểm tra với các bộ dữ liệu lớn
hơn. Nhóm nghiên cứu nghĩ rằng những kiến thức thu được sẽ là hành trang và sẽ giúp
ích rất nhiều cho mỗi thành viên trong quá trình phát triển về sau.
Đề tài Lý thuyết đồ thị Nhóm 4 – Lớp PP Toán Sơ Cấp K24
TÀI LIỆU THAM KHẢO

[1] TS Trần Quốc Chiến, Giáo trình Toán rời rạc.
[2] TS Trần Quốc Chiến, Giáo trình Lý thuyết đồ thị, Đại học sư phạm – Đại học
Đà Nẵng, 2007.
[3] Đỗ Đức Giáo, Toán Rời rạc, Nhà xuất bản Giáo dục, 2005.
[4] Đặng Huy Ruận, Lý thuyết đồ thị và ứng dụng, Nhà xuất bản Khoa học và Kỹ
thuật - Hà Nội, 2000.
[5] Các tài liệu trên Internet.
Đường đi Hamilton và bài toán người du lịch 21

×