You are on page 1of 23

ĐẠI HỌC TÔN ĐỨC THẮNG

Giáo trình thực hành


Lý thuyết đồ thị
BẢN THẢO

3/1/2009
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

MỤC LỤC

Chương 1: Biểu diễn đồ thị trên máy tính......................................................................................................................... 3

Chương 2: Liên thông và các thành phần liên thông ........................................................................................................ 8

Chương 3: Cây và cây bao trùm...................................................................................................................................... 12

Chương 4: Thuật toán Prim............................................................................................................................................. 14

Chương 5: Cây bao trùm nhỏ nhất & Thuật toán Kruskal .............................................................................................. 16

Chương 6: Tìm đường trong đồ thị ................................................................................................................................. 18

Chương 7: Tìm đường trong đồ thị - Thuật toán Dijkstra ............................................................................................... 20

Chương 8: Tìm đường trong đồ thị - Thuật toán Floyd .................................................................................................. 22

2
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 1: Biểu diễn đồ thị trên máy tính

I. NHẮC LẠI LÝ THUYẾT


1. MA TRẬN KỀ (ADJACENCY MATRIX)
Giả sử là một đơn đồ thị có số đỉnh là . Không mất tính tổng quát, có
thể coi các đỉnh được đánh số . Khi đó, ta có thể biểu diễn đồ thị bằng một
ma trận vuông cấp n. Trong đó:
- nếu
- nếu
Đối với đa đồ thị thì lưu trữ số cạnh nối giữa đỉnh và đỉnh .
Ví dụ

Các tính chất của ma trận kề


Cho đồ thị G = (V, E) và ma trận kề tương ứng A
- Nếu G là đồ thị vô hướng, thì:
o A là ma trận đối xứng
o Tổng các số trên hàng i = Tổng các số trên cột i = Bậc của đỉnh i
- Nếu G là đồ thị có hướng, thì
o Tổng các số trên hàng i = Bậc ra của đỉnh i
o Tổng các số trên cột i = Bậc vào của đỉnh i
Ưu điểm:
- Đơn giản, trực quan, dễ cài đặt
- Dễ kiểm tra sự liền kề của 2 đỉnh bất kì bằng phép so sánh
Khuyết điểm:
- Chi phí lưu trữ luôn luôn cố định là
- Chi phí duyệt các cạnh kề của đỉnh i luôn luôn cố định là .

3
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

2. DANH SÁCH CẠNH (EDGE LIST)


Trong trường hợp đồ thị có n đỉnh, m cạnh, ta có thể biểu diễn đồ thị dưới dạng danh
sách cạnh bằng cách liệt kê tất cả các cạnh của đồ thị trong một danh sách, mỗi phần
tử của danh sách là một cặp tương ứng với một cạnh của đồ thị. Danh sách này
có thể được cài đặt bằng mảng hoặc danh sách liên kết.
Đối với đa đồ thị thì lưu trữ số cạnh nối giữa đỉnh và đỉnh .
Ví dụ

Cài đặt danh sách cạnh bằng mảng:

Cài đặt danh sách cạnh bằng danh sách liên kết:

Các tính chất của danh sách cạnh


Ưu điểm:
- Tiết kiệm chi phí lưu trữ trong trường hợp đồ thị thưa.
Khuyết điểm:
- Chi phí duyệt các cạnh kề của đỉnh i lớn đối với trường hợp đồ thị dày.
3. DANH SÁCH KỀ (ADJACENCY LIST)
Để khắc phục nhược điểm của phương pháp biểu diễn đồ thị bằng ma trận kề và danh
sách cạnh, người ta đề xuất phương pháp biểu diễn đồ thị bằng danh sách kề. Trong
cách biểu diễn này, với mỗi đỉnh v của đồ thị, ta lưu trữ tương ứng với một danh sách
chứa các đỉnh kề với nó.
Ví dụ

4
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Danh sách kề tương ứng

Hoặc:

Các tính chất của danh sách kề


Ưu điểm:
- Tiết kiệm chi phí lưu trữ.
- Tiết kiệm chi phí duyệt đỉnh kề hoặc cạnh kề của một đỉnh bất kì.
Khuyết điểm:
- Cài đặt phức tạp.
- Chi phí kiểm tra hai đỉnh bất kì có kề nhau hay không “yếu” hơn ma trận kề.
II. THỰC HÀNH
1. CÀI ĐẶT MA TRẬN KỀ
Định dạng dữ liệu
Dữ liệu vào ma trận kề được lưu trong một tập tin văn bản có cấu trúc như sau:
- Dòng đầu tiên chứa số nguyên n, cho biết số đỉnh của đồ thị
- n dòng tiếp theo, mỗi dòng chứa n số nguyên ứng với các phần tử trong ma trận kề.

5
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Tổ chức dữ liệu
Để biểu diễn ma trận, ta dùng một mảng hai chiều kiểu nguyên.
#define MAX 100
struct GRAPH {
int n;
int a[MAX][MAX];
};
Đọc ma trận kề từ tập tin vào mảng
void ReadGraph(GRAPH &g, char *fn) {
// mở file, nếu không mở được file sẽ báo lỗi và thoát
FILE * f = fopen(fn, “rt”);
if (f == NULL) {
exit(0);
}
// đọc giá trị đỉnh của đồ thị vào biến n
fscanf(f, “%d”, &g.n);
// đọc giá trị của ma trận a từ file
int i, j;
for (i=0; i<g.n; i++)
for (j=0; j<g.n; j++)
fscanf(f, “%d”, &g.a[i][j]);
// đóng file nhập
fclose(f);
}
Xuất ma trận kề ra màn hình
void PrintGraph(GRAPH &g) {
// in ra số đỉnh của đồ thị
printf(“%d\n”, g.n);
for (int i=0; i<g.n; i++) {
for (int j=0; j<g.n; j++)
printf(“%d\t”, g.a[i][j]);
printf(“\n”);
}
}
2. CÀI ĐẶT DANH SÁCH CẠNH VÀ DANH SÁCH KỀ
Xem như bài tập

6
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

III. BÀI TẬP


1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP theo
định dạng ở phần 2. In ra màn hình các giá trị sau đây:
a. Số cạnh, số đỉnh của đồ thị.
b. Các đỉnh có số bậc lớn nhất.
c. Các đỉnh có số bậc nhỏ nhất.
d. Các đỉnh cô lập.
e. Các đỉnh treo.
2. Viết chương trình đ ọc ma trận kề của đồ thị G từ tập tin GRAPH.INP, cho biết đồ thị
G có hướng hay vô hướng.
3. Viết chương trình đ ọc ma trận kề của đồ thị G từ tập tin GRAPH.INP, cho biết đồ thị
có tồn tại chu trình Euler hay không? Nếu có, hãy chỉ ra một chu trình Euler của đồ thị
này.
4. Viết chương trình đọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.AM theo
định dạng ở phần 2. In ra tập tin GRAPH.EL theo định dạnh danh sách cạnh.
5. Viết chương trình đ ọc danh sách cạnh của đồ thị vô hướng G từ tập tin GRAPH.EL
theo định dạng ở phần 2. In ra tập tin GRAPH.AM theo định dạnh ma trận kề.
6. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.IN theo
định dạng ở phần 2. In ra tập tin GRAPH.AL theo định dạnh danh sách kề.
7. Viết chương trình đọc danh sách kề của đồ thị vô hướng G từ tập tin GRAPH.AL theo
định dạng ở phần 2. In ra tập tin GRAPH.AM theo định dạnh ma trận kề.
8. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G và in đồ thị ra màn hình đ ồ
họa.

7
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 2: Liên thông và các thành phần liên thông

I. NHẮC LẠI LÝ THUYẾT


1. ĐỒ THỊ LIÊN THÔNG
Đối với đồ thị có hướng, ta có hai khái niệm liên thông mạnh và liên thông yếu. Việc
kiểm tra một đồ thị có hướng có liên thông yếu hay không về độ phức tạp tương
đương với việc kiểm tra một đồ thị vô hướng có liên thông hay không. Bài hướng dẫn
thực hành chỉ tập trung vào việc bài toán kiểm tra một đồ thị vô hướng có liên thông
hay không.
Nếu như một đồ thị là không liên thông, ta sẽ có được nhiều miền liên thông (giữa các
đỉnh trong cùng một miền liên thông có đường đi trực tiếp/gián tiếp đến nhau; giữa
các đỉnh khác miền liên thông không tồn tại đường đi này). Như vậy, một đồ thị liên
thông là một đồ thị chỉ có duy nhất một miền liên thông.
2. THUẬT TOÁN TÌM SỐ THÀNH PHẦN LIÊN THÔNG
Bước 1: Đánh dấu tất cả các đỉnh trong đồ thị là chưa duyệt. Biến đếm số thành phần
liên thông được gán là 0.
Bước 2: Chọn một đỉnh i bất kỳ chưa được duyệt, sử dụng một hàm Visit() để duyệt
đỉnh i và tất cả các đỉnh j có nối với đỉnh i. Trong quá trình duyệt, ta sẽ đánh dấu các
đỉnh này là đã được duyệt để sau này không xét trở lại nữa. Kết thúc lần duyệt này, ta
được một miền liên thông.
Để có thể dễ dàng in lại từng thành phần liên thông, ta sử dụng nhãn đánh dấu cho
biết một đỉnh sẽ thuộc miền liên thông thứ mấy trong quá trình duyệt.
Bước 3: Tăng biến đếm số thành phần liên thông.
Nếu vẫn còn một đỉnh nào chưa được duyệt, ta quay lại bước 2.
Bước 4: Nếu số thành phần liên thông là 1, in ra đồ thị liên thông.
Ngược lại, in ra số thành phần liên thông.
Nếu muốn in ra từng thành phần liên thông, ta sử dụng nhãn đã gán ở trên, các đỉnh
có cùng nhãn sẽ được xem là cùng một miền liên thông.
II. CÀI ĐẶT
// nhãn cho các đỉnh, 0 là chưa duyệt

8
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
// 1, 2, … là đã duyệt và đỉnh thuộc miền liên thông tương ứng
int visited[MAX];
int nconnect;

void solve(GRAPH& g) {
// khởi tạo nhãn cho tất cả các đỉnh là chưa duyệt
// đặt số miền liên thông ban đầu la 0
nconnect = 0;
for (int i=0; i<g.n; i++)
visited[i] = 0;
// lặp để tìm đỉnh chưa xét, gọi hàm visit cho đỉnh này

for (int i=0; i<g.n; i++)


if (visited[i] == 0) {
visit(g, i, nconnect);
nconnect++;
}
}

void visit(GRAPH& g, int i, int nconnect)


{
// gán nhãn nconnect cho đỉnh i
visited[i] = nconnect;
// duyệt các đỉnh j chưa được duyệt và có nối với i
for (int j=0; j<g.n; j++)
if ((visited[j] == 0) && (g.a[i][j] != 0)) {
previous[j] = i;
visit(g, j, nconnect);
}
}

III. BÀI TẬP


1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP, cho
biết đồ thị G có liên thông hay không? Nếu không thì cho biết G có bao nhiêu thành
phần liên thông.
2. Thực hiện yêu cầu của câu 1 nhưng sử dụng cấu trúc hàng đợi để khử đệ quy.
3. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP, hãy
chỉ cách thêm vào G số cạnh tối thiểu để G liên thông.

9
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
4. Một chuyên gia tin học muốn viết một chương trình xác đ ịnh số đối tượng trong một
tấm ảnh nhị phân. Một đối tượng được xác định gồm các điểm đen liên thông bốn với
nhau trong ma trận ảnh. Hãy giúp chuyên gia tin học đếm số đối tượng này.
Dữ liệu: Dữ liệu vào từ file văn bản BITMAP.INP
- Dòng đầu tiên ghi 2 số tự nhiên N và M (1 < N, M <=250) tương ứng số lượng
chiều dài và chiều rộng của tấm ảnh.
- Dòng thứ I trong N dòng tiếp theo chứa M số nhị phân 0 (trắng) hoặc 1 (đen).

Kết quả: Kết quả ghi ra file văn bản BITMAP.OUT như sau:

- Ghi số K là tổng số đối tượng trong ảnh


Ví dụ:

BITMAP.INP BITMAP.OUT
55 4
10 0 0 1
10 0 1 1
11 0 1 1
10 1 0 0
10 0 1 1

5. Một cơ quan có N nhân viên được đánh số thứ tự từ 1 đến N. Mỗi người có một phòng
làm việc riêng của mình . Do nhu cầu công việc , hằng ngày mỗi nhân viên có thể phải
tiếp xúc với một số nhân viên khác . Vào một ngày làm việc bình thường , có một nhân
viên bị nhiễm SARS , nhưng do không biết nên người này vẫn đi làm . Đến cuối ngày
làm việc người ta mới phát hiện ra người nhiễm bệnh SARS đầu tiên . Khả năng lây
lan của SARS rất nhanh chóng : một người nhiễm bệnh SARS nếu tiếp xúc với một
người khác có thể sẽ truyền bệnh cho người này.
Yêu cầu: Hãy giúp các bác sĩ kiểm tra xem cuối ngày hôm đó , có bao nhiêu người có
thể nhiễm bệnh và đó là những người nào để còn cách ly . Người có tiếp xúc với người
nhiễm bệnh được coi là người nhiễm bệnh
Dữ liệu: Dữ liệu vào từ file văn bản SARS.INP

10
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
- Dòng đầu tiên ghi 2 số tự nhiên N và K (1 < N <=250, 1<=K<=N) tương ứng số
lượng người làm việc trong tòa nhà và số hiệu của nhân viên đã nhiễn SARS đ ầu
tiên.
- Dòng thứ I trong N dòng tiếp theo ghi danh sách n hững người có tiếp xúc với
người thứ I theo cách sau : số đầu tiên J của dòng là tổng số nhân viên đã gặp
người thứ I, tiếp theo là J số tự nhiên lần lượt là số hiệ u của các nhân viên đó . Nếu
J=0 có nghĩa là không ai đã tiếp xúc với người I
Kết quả: Kết quả ghi ra file văn bản SARS.OUT như sau:
- Dòng đầu tiên ghi số S là tổng số người có thể bị lây nhiễm SARS
- Dòng thứ 2 liệt kê tất cả các người có thể bị lây nhiễm SARS cần cách ly , danh
sách cần được sắp xếp theo thứ tự tăng dần của số hiệu nhân viên
Trong các file dữ liệu và kết quả , các số trên cùng một dòng cách nhau ít nhất một
dấu cách.
Ví dụ:

SARS.INP SARS.OUT
51 3
223 123
213
12
15
14

11
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 3: Cây và cây bao trùm

I. NHẮC LẠI LÝ THUYẾT


Cây là đồ thị vô hướng, liên thông và không có chu trình đơn. Cây bao trùm của đồ thị vô
hướng liên thông G là cây chứa tất cả các đỉnh của G. Cây bao trùm của đồ thị có thể được
xác định bằng các cách sau:
• Xác định cây bao trùm bằng thuật toán hợp nhất: Xuất phát từ tập T chứa n đỉnh của
G và không chứa cạnh nào, ta lần lượt thêm dần các cạnh trong G vào T sao cho
không tạo ra chu trình. Đến khi đạt n-1 cạnh, T là cây bao trùm của G.
• Xác định cây bao trùm bằng thuật toán tìm kiếm: Áp dụng thuật giải duyệt đồ thị,
ghi nhận các cạnh đã duyệt qua. Tập các cạnh này sẽ tạo thành cây bao trùm.
II. CÀI ĐẶT
Sử dụng thuật giải duyệt đồ thị đã đề cập ở bài 2 để xác định cây bao trùm. Tập các cạnh nối
từ đỉnh i và previous[i] chính là cây bao trùm cần tìm.
III. BÀI TẬP
1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP, cho
biết đồ thị G có liên thông hay không? Nếu có hãy tìm ra một cây bao trùm của G và
in ra file GRAPH.OUT các cạnh của cây bao trùm này.
2. Trong các lâu đài cổ ở châu Âu người ta thường xây dựng các đường hầm bí mật để
thoát hiểm trong các trường hợp khẩn cấp. Các đường hầm chỉ có thể vào từ một cửa
vào duy nhất tại phòng Trung tâm và thoát ra ở rất nhiều của ra. Các cửa ra đều nằm ở
rìa lâu đài, do v ậy, nếu thoát ra được rìa lâu đài thì coi như đã thoát hi ểm. Để nguỵ
trang, người ta cho đào nhiều nhánh hầm cụt và cửa vào giả. Ngoài ra, để tăng khả
năng thoát hiểm, người ta còn xây dựng các đường hầm giao nhau tại một số vị trí. Để
nghiệm thu công trình, chủ lâu đài cần kiểm tra xem từ phòng trung tâm có thể thoát
hiểm qua hệ thống đường hầm hay không. Hãy lập trình giúp chủ lâu đài kiểm tra hệ
thống trên. Biết rằng lâu đài là một hình vuông được chia lưới ô vuông gồm n dòng, n
cột. Trên hoạ đồ, ô ở dòng i cột j được ghi số 1 nếu có đường hầm, số 0 nếu không có
(ô ở góc trên trái có toạ độ (0, 0)). 2 ô chỉ có thể thông nhau nếu chúng có chung cạnh.

12
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
Dữ liệu nhập vào từ tập tin văn bản DUONGHAM.IN gồm:
• Dòng đầu chứ 3 số nguyên dương n (n<=50), D, C (D, C là dòng và c ột của phòng
trung tâm).
• N dòng tiếp theo, mỗi dòng chứa n số là các số ở các vị trí tương ứng trên hoạ đồ.

Kết quả tìm được ghi ra tập tin văn bản DUONGHAM.OUT. Dòng đầu chứa số m là
số ô phải đi qua, nếu không thoát được thì m=-1. Trong trường hợp thoát được, m
dòng tiếp theo, m dòng tiếp theo: mỗi dòng chứa 2 số là số hiệu dòng cột của các ô
phải đi qua theo đúng trình tự của một cách thoát hiểm.

Ví dụ:

DUONGHAM.IN DUONGHAM.OUT
4 2 1 3
0 1 1 0 2 1
1 0 0 1 2 2
1 1 1 1 3 2
0 1 1 0

13
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 4: Thuật toán Prim

I. NHẮC LẠI LÝ THUYẾT


Thuật toán Prim được sử dụng để tìm cây bao trùm tối tiểu của đồ thị theo phương pháp
duyệt đồ thị đã đề cập ở bài 3, các bước thực hiện của thuật toán như sau:
Bước 1: Đánh dấu tất cả các đỉnh của đồ thị là chưa duyệt.
Bước 2: Chọn một đỉnh bất kỳ làm gốc của cây bao trùm, để thống nhất ta chọn đỉnh 0, đánh
dấu đỉnh này là đã duyệt.
Bước 3: Chọn cạnh (x, y) có trọng số nhỏ nhất nối giữa vùng đỉnh đã duyệt và vùng đỉnh
chưa duyệt (x được đánh dấu là đã duyệt và y được đánh dấu là chưa duyệt). Nếu không tìm
được một cạnh nào như thế, dừng thuật toán.
Bước 4: Cập nhật cạnh (x, y) vào cây bao trùm.
Bước 5: Đánh dấu đỉnh y là đã duyệt.
Bước 6: Quay lại bước 3.
II. CÀI ĐẶT
void prim(GRAPH& g)
{
// gán số cạnh của cây bao trùm n ban đầu là 0

// khởi tạo nhãn của các đỉnh là duyệt (0)

// đánh dấu đỉnh 0 là đã duyệt

while (n < g.n - 1) // số cạnh tối đa của cây bao trùm
{
int imin, jmin, emin = MAXINT;
// chọn đỉnh i đã duyệt
for (int i = 0; i < g.n; i++)
if (visited[i] == 1) {

// chọn đỉnh j chưa duyệt


for (int j = 0; j < g.n; j++)
if (visited[j] == 0) {

// tìm cạnh nhỏ nhất nối giữa i, j


if (g.a[i][j] < emin) {
emin = g.a[i][j];
imin = i; jmin = j;
}
}
}
// cập nhật (i,j) vào cây bao trùm

14
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
T.a[i][j] = 1; T.a[j][i] = 1;
visited[j] = 1;
n++;
}
}
III. BÀI TẬP
1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP, cho
biết đồ thị G có liên thông hay không? Nếu có hãy tìm ra một cây bao trùm nhỏ nhất
của G và in ra file GRAPH.OUT các cạnh của cây bao trùm này.
2. Viết chương trình th ực hiện yêu cầu của câu 1 với cách cài đặt tối ưu bằng cách sử
dụng PriorityQueue.

15
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 5: Cây bao trùm nhỏ nhất & Thuật toán Kruskal

I. NHẮC LẠI LÝ THUYẾT


Thuật toán Kruskal là thuật toán tìm cây bao trùm theo phương pháp hợp nhất đã
đề cập ở bài 3, các bước chi tiết của thuật toán như sau:
Bước 1: Tạo danh sách cạnh edges. Đánh dấu tất cả các cạnh là chưa duyệt.

Bước 2: Sắp xếp lại các cạnh theo thứ tự trọng số tăng dần.

Bước 3: Khởi tạo nhãn root, với root[i] = i (root[i] biểu diễn đỉnh gốc của i
trong cây chứa i)

Bước 4: Chọn ra cạnh e = (x,y) nhỏ nhất chưa duyệt trong danh sách cạnh.
Nếu không có một cạnh e nào như thế, dừng thuật toán  thất bại

Bước 5: Đánh dấu cạnh e là đã duyệt. Nếu nhãn của root[e.x] và root[e.y]
giống nhau (nghĩa là chúng thuộc cùng miền liên thông, việc chọn cạnh e sẽ
làm phát sinh chu trình), quay lại bước 4.

Bước 6: Cập nhật cạnh e vào cây bao trùm, nếu số cạnh đã chọn n-1, dừng
thuật toán  thành công! Cập nhật tất cả các đỉnh có root là root[e.y] thành
root[e.x].
Quay lại bước 4.

II. CÀI ĐẶT


GRAPH kruskal(GRAPH g) {

// khởi tạo danh sách cạnh


...
// sắp xếp danh sách cạnh tăng dần theo trọng số
...
// khởi tạo mảng root[i] = i;
...
// lần lượt đưa các cạnh vào cây để không tạo ra chu trình
while (n < g.n-1){
do {
// chọn cạnh nhỏ nhất chưa duyệt
int i = edge[k][i];
int j = edge[k][j];
// kiểm tra khi thêm cạnh nào vào cây có tạo ra chu trình
int ri = i; while (root[ri] != -1) ri = root[ri];

16
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
int rj = j; while (root[rj] != -1) rj = root[rj];
if (ri != rj) {
io = i; jo = j;
rio = ri; rjo = rj;
break;
}
k++;
} while (k < ne);
n++;
T.a[i][j] = T.a[j][i] = g.a[i][j];
root[rj] = ri;
}

III. BÀI TẬP


1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP, cho
biết đồ thị G có liên thông hay không? Nếu có hãy tìm ra một cây bao trùm nhỏ nhất
của G theo thuật toán Kruskal và in ra file GRAPH.OUT các cạnh của cây bao trùm
này.
2. Thực hiện yêu cầu của câu 1 sử dụng thuật toán QuickSort để sắp xếp các cạnh trước
khi xử lí thuật toán.

17
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 6: Tìm đường trong đồ thị

I. NHẮC LẠI LÝ THUYẾT


Bài toán tìm đường đi từ một đỉnh x đến một đỉnh y dù cài đặt dưới bất kỳ một thuật
toán nào cũng phải đáp ứng được yêu cầu sau đây: có tồn tại đường đi hay không?
Nếu có phải chỉ ra được đường đi cụ thể và chi phí của đường đi (tổng trọng số các
cung đi qua).

Xem lại thuật toán kiểm tra miền liên thông chúng ta đã làm, việc kiểm tra xem có tồn
tại một đường đi (kể cả có hướng và vô hướng) từ đỉnh x đến đỉnh y hay không hoàn
toàn có thể làm được với hàm viếng duyệt. Tuy nhiên nếu muốn chỉ ra một đường đi
cụ thể, ta phải thực hiện lại việc lưu vết (lưu đỉnh cha).

Để tìm đường trong đồ thị, ta có thể sử dụng phương pháp duyệt đồ thị theo chiều sâu
hoặc phương pháp duyệt đồ thị theo chiều rộng. Về cơ bản, hai phương pháp này có
thể xem là tương tự nhau (đồng thời tương tự phương pháp duyệt tìm thành phần liên
thông đã nêu ở chương 2). Nếu cài đặt bằng phương pháp khử đệ quy, điểm khác biệt
giữa hai phương pháp này là cấu trúc dữ liệu được sử dụng. Duyệt theo chiều sâu sử
dụng STACK còn duyệt theo chiều rộng sử dụng QUEUE.

II. CÀI ĐẶT


1. Cài đặt DFS
void visit(GRAPH& g) {
// tạo một ngăn xếp được khởi tạo chỉ gồm một đỉnh i
stack[0] = i;
stackcount = 1; // chỉ có một phần tử trong ngăn xếp

// duyệt tất cả các đỉnh trong ngăn xếp


while (stackcount > 0)
{
// lấy đỉnh x từ hàng đợi
stackcount--;
i = stack[stackcount];
// xét tất cả đỉnh j chưa được duyệt và có nối với i
for (j…)
if (…)
{
// đưa j vào ngăn xếp
stack[stackcount] = j;

18
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
stackcount++;
// gán đường đi cho j
previous[j] = i;

}
}
}

2. Cài đặt BFS


void visit(GRAPH& g) {
// tạo một hàng đợi được khởi tạo chỉ gồm một đỉnh i
queue[0] = i;
queuecount = 1; // chỉ có một phần tử trong hàng đợi
queueindex = 0; // chỉ số đỉnh đang duyệt trong hàng đợi


// duyệt tất cả các đỉnh trong hàng đợi
while (queueindex < queuecount)
{
// lấy đỉnh x từ hàng đợi
x = queue[queueindex];
queueindex++;
// xét tất cả đỉnh j chưa được duyệt và có nối với x
for (j…)
if (…)
{
// đưa j vào hàng đợi
queue[queuecount] = j;
queuecount++;
// gán đường đi cho j
previous[j] = i;

}
}
}

III. BÀI TẬP


1. Viết chương trình đ ọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP và in
đường đi từ đỉnh I đến đỉnh J bất kì nhập từ bàn phím sử dụng thuật toán tìm kiếm
theo chiều sâu.
2. Viết chương trình đọc ma trận kề của đồ thị vô hướng G từ tập tin GRAPH.INP và in
đường đi từ đỉnh I đến đỉnh J bất kì nhập từ bàn phím sử dụng thuật toán tìm kiếm
theo chiều rộng.
3. Thay đổi cấu trúc dữ liệu để tạo thành thuật toán tìm kiếm ngẫu nhiên. (hướng dẫn:
tạo danh sách chờ duyệt, và phép lấy phần tử ra khỏi danh sách là chọn ngẫu nhiên
một phần tử trong đó).

19
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 7: Tìm đường trong đồ thị - Thuật toán Dijkstra

I. NHẮC LẠI LÝ THUYẾT


Thuật toán Dijkstra có thể mô tả như sau:
Ta quản lý một tập hợp động S. Ban đầu S={s}.
Với mỗi đỉnh v, chúng ta quản lý một nhãn d[v] là độ dài bé nhất trong các đường đi từ
nguồn s đến một đỉnh u nào đó thuộc S, rồi đi theo cạnh nối u-v.
Trong các đỉnh ngoài S, chúng ta chọn đỉnh u có nhãn d[u] bé nhất, bổ sung vào tập S.
Tập S được mở rộng thêm một đỉnh, khi đó chúng ta cần cập nhật lại các nhãn d cho phù
hợp với định nghĩa.
Thuật toán kết thúc khi toàn bộ các đỉnh đã nằm trong tập S, hoặc nếu chỉ cần tìm đường
đi ngắn nhất đến một đỉnh đích t, thì chúng ta dừng lại khi đỉnh t được bổ sung vào tập S.
II. CÀI ĐẶT
1. Cài đặt Dijkstra sử dụng mảng
void dijkstra(GRAPH g){
int T[MaxV];
for (int v=0; v<V; v++)
{
d[v] = A[s][v];
previous[v] = s;
}
d[s] = 0; T[s] = 1;
while ( 1 )
{
int u = -1;
for (int z=0; z<V; z++)
if (T[z] != 1 && (u==-1 || d[u] > d[z]))
u = z;
if (u == -1)
break;
T[u] = 1;
for (int v=0; v<V; v++)
if (T[v]!=1 && d[v]>d[u]+A[u][v])
{
d[v] = d[u]+A[u][v];
previous[v] = u;
}
}
}
2. Cài đặt Dijkstra sử dụng Heap
Sinh viên tự cài đặt theo mã giả sau
Khởi tạo tập S chỉ chứa đỉnh ban đầu s;

20
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
for (mỗi đỉnh v thuộc G) {
D[v] = C(s, v);
}
D[s] = 0;
while ( (V-S) != Φ ) {
Chọn đỉnh u thuộc (V-S) sao cho D[u] ngắn nhất;
S = S U {u};
for ( mỗi v thuộc (V-S) ) {
if (D[u] + C(u, v) < D[v]) {
D[v] = D[u] + C(u, v);
}
}
}

III. BÀI TẬP


1. Cài đặt thuật toán Dijkstra
2. B là anh chàng tài hoa nổi tiếng của trường NAM, G là hoa khôi của trường NỮ. B và
G quen nhau đã lâu. Không bi ết từ lúc nào cả hai đã tuân thủ một qui ước: mỗi chiều
đi học về cần gặp nhau tại một địa điểm do G chọn (có thể là rạp chiếu bóng, quán
kem hay đơn giản chỉ là góc phố quen) sau đó về thẳng nhà của mỗi người. Nhiệm vụ
của B là cần chỉ ra đường đi ngắn nhất cho cả hai người để từ trường đến điểm hẹn
sau đó về nhà – dĩ nhiên, B và M đang học 2 trường khác nhau và ở 2 nhà cũng khác
nhau. Cả hai đều sinh sống và học tập ở thành phố A. B đang học chuyên ngành
CNTT nên quyết định số hóa bản đồ thành phố. Thành phố A có N địa điểm được B
đánh số từ 1 đến N, trường NAM là địa điểm U, trường NỮ là địa điểm V, nhà B là
địa điểm Z, nhà G là địa điểm T, điểm hẹn là địa điểm X. B cũng đã kh ảo sát và ghi
nhận lại khoảng cách giữa các địa điểm có đường nối cũng như đánh dấu các cặp địa
điểm không có đường nối trực tiếp. Bạn hãy giúp B lập trình để có thể xác định đường
đi cho B và M sao cho tổng quãng đư ờng cả hai người phải đi qua là bé nhất – vì B
chưa học LTĐT.
Dữ liệu vào được cho trong tập tin văn bản HENHO.IN gồm
- Dòng đầu tiên ghi số nguyên N là số địa điểm của thành phố A (N <= 100).
- N dòng tiếp theo, mỗi dòng ghi N số thực. Số ở dòng I cột J cho biết khoảng
cách đoạn đường nối từ I đến J và được ghi là 0 nếu không có đường nối.
- Dòng cuối cùng ghi 5 số U, V, Z, T, X
Kết quả tìm được ghi vào tạp tin văn bản HENHO.OUT ghi một số thực là tổng độ dài
quảng đường mà B và G phải đi qua và được ghi là -1 nếu không tìm đư ợc đường đi
thỏa yêu cầu.

21
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng

Chương 8: Tìm đường trong đồ thị - Thuật toán Floyd

I. NHẮC LẠI LÝ THUYẾT


Cho đơn đồ thị có hướng, có trọng số G=(V,E) với n đỉnh và m cạnh, Ma trận trọng số
C[u,v]. Bài toán đặt ra là tính tất cả các d(u,v) là khoảng cách nhỏ nhất từ u đến v.
Ta sử dụng thuật toán FLOYD để giải quyết bài toán này. Từ ma trận trọng số ban đầu C, ta
tính lại các C[u,v] thành đường đi ngắn nhất từ u đến v theo công thức:
C[u,v] := min (C[u,v], C[u,k] C[k,v]) với mọi đỉnh k xét từ 1 đến n.
Tức là: Nếu đường đi đang có từ u đến v dài hơn đường đi từ u đến k cộng với đường đi từ k
đến v thì ta ghi nhận lại đường đi ngắn nhất (hiện có) là đường đi qua k.
for k:=1 to n do
for u:=1 to n do
for v:=1 to n do
c[u,v] := min ( c[u,v] , c[u,k] c[k,v] );
II. CÀI ĐẶT
void Floyd (GRAPH g){
int i, j, k;
int **Cost; Cost = new int *[g.n];
for (i = 0; i < gr.nV; i++) Cost[i] = new int [g.n];
int **Previous; Previous = new int *[g.n];
for (i = 0; i < g.n; i++) Previous[i] = new int [g.n];
for (i = 0; i < g.n; i++)
for (j = 0; j < g.n; j++){
Cost[i][j] = g.a[i][j];
Previous[i][j] = i;
}
for (k = 0; k < g.n; k++)
for (i = 0; i < g.n; i++)
for (j = 0; j < g.n; j++)
if (Cost[i][j] > Cost[i][k] + Cost[k][j]){
Cost[i][j] = Cost[i][k] + Cost[k][j];
Previous[i][j] = Previous[k][j];
}
}
III. BÀI TẬP

22
Bài tập thực hành lý thuyết đồ thị Khoa CNTT-TUD, ĐH Tôn Đức Thắng
1. Cài đặt thuật toán Floyd
2. Cho đồ thị vô hướng G liên thông. Tìm một đỉnh sao cho đường đi dài nhất từ vị trí đó
tới các đỉnh còn lại là ngắn nhất.
3. Cho 1 điểm O và n điểm trong mặt phẳng tọa độ. Từ n điểm đó, xây 1 hàng rào bao
quanh điểm O sao cho chu vi hàng rào là nhỏ nhất. (O không nằm trên cạnh của hàng
rào).

23

You might also like