Tải bản đầy đủ
Chương 2. MỘT SỐ BÀI TOÁN TỐI ƯU LỚP P, NP-C TRONG ĐỒ THỊ

Chương 2. MỘT SỐ BÀI TOÁN TỐI ƯU LỚP P, NP-C TRONG ĐỒ THỊ

Tải bản đầy đủ

23

Lần lượt ghép thêm vào cạnh có trọng số tối thiểu và không tạo thành chu
trình với các cạnh đã được chọn. Thuật toán dừng sau khi (n-1) cạnh đã được
chọn.
Giả sử ta cần tìm cây bao trùm nhỏ nhất của đồ thị G. Thuật toán bao gồm
các bước sau:
• Khởi tạo rừng F (tập hợp các cây), trong đó mỗi đỉnh của G tạo thành một
cây riêng biệt
• Khởi tạo tập S chứa tất cả các cạnh của G
• Chừng nào S còn khác rỗng và F gồm hơn một cây
 Xóa cạnh nhỏ nhất trong S
 Nếu cạnh đó nối hai cây khác nhau trong F, thì thêm nó vào F và hợp hai
cây kề với nó làm một
 Nếu không thì loại bỏ cạnh đó.
Khi thuật toán kết thúc, rừng chỉ gồm đúng một cây và đó là một cây bao
trùm nhỏ nhất của đồ thị G.
Thuật toán Kruskal [2]
Procedure Kruskal (G: đồ thị V đỉnh, liên thông, có trọng số)
T:= đồ thị rỗng
for i := 1 to n-1
begin
e := một cạnh bất kì của G với trọng số nhỏ nhất và không tạo ra chu trình
trong T, khi ghép nó vào T.
T:= T với cạnh e đã được ghép thêm vào.
end { T là cây khung nhỏ nhất}
Độ phức tạp [6]
Nếu E là số cạnh và V là số đỉnh của đồ thị thì thuật toán Kruskal chạy
trong thời gian O(E log V).
Ví dụ 2.1. Cho đồ thị G như hình 2.2, yêu cầu tìm ra cây khung nhỏ nhất của
đồ thị G ?

24

Hình 2.2. Đồ thị có trọng số G
G gồm có 8 đỉnh
Đồ thị G có n phần tử. Thuật toán Kruskal sẽ dừng khi có n-1 trong tập hợp T
n=8
Vậy số cạnh trong tập hợp T: n - 1 = 8 - 1 = 7
Bước 1: Bảng 2.1: Sắp xếp các cạnh theo thứ tự trọng số tăng dần có:
Cạnh

Trọng số

Cạnh (1,5)

1

Cạnh (4, 8)

1

Cạnh (7,8)

1

Cạnh (1, 6)

2

Cạnh (2, 3)

2

Cạnh (3, 8)

3

Cạnh (1, 3)

4

Cạnh (3, 7)

4

Cạnh (4, 5)

5

Cạnh (4, 6)

5

Cạnh (1, 4)

6

Cạnh (5, 6)

6

Cạnh (2, 4)

7

Cạnh (6, 8)

7

Cạnh (1, 2)

8

Cạnh (6, 7)

8

Cạnh (4, 3)

9

và khởi tạo T := Ø.
Bước 2: Duyệt theo cạnh e thuộc danh sách đã sắp xếp
+ Vì T + {(1, 5)} không chứa
chu trình thì ghép cạnh (1,5)
vào cây T:= T + {(1,5)}.

25

+ Vì T + {(4, 8)} không chứa
chu trình thì ghép cạnh (4,8)
vào cây T:= T + {(4, 8)}

+ Vì T + {(7, 8)} không chứa
chu trình thì ghép cạnh (7,8)
vào cây
T:= T + {(7, 8)}

+ Vì T + {(1, 6)} không chứa
chu trình thì ghép cạnh (1,6)
vào cây
T:= T + {(1, 6)}

+ Vì T + {(2, 3)} không chứa
chu trình thì ghép cạnh (2,3)
vào cây
T:= T + {(2, 3)}

26

+ Vì T + {(3, 8)} không chứa
chu trình thì ghép cạnh (3,8)
vào cây
T:= T + {(3, 8)}

+ Vì T + {(1, 3)} không chứa
chu trình thì ghép cạnh (1,3)
vào cây
T:= T + {(1, 3)}

+ Vì T có đủ n -1 cạnh ( 7 cạnh) nên dừng
Cây khung cần tìm có 7 cạnh (đã chọn) và tổng độ dài các cạnh là: 14.
2.2. Bài toán tìm đường đi ngắn nhất -Thuật toán Dijkstra [2]
Trước khi trình bày thuật toán ta xét một ví dụ minh họa.
Ví dụ 2.2. Tính độ dài của đường đi ngắn nhất giữa a và z trong đồ thị có
trọng số cho trên hình 2.3

b
a
2
4
3
d
c
z
e
3

27

3
1
2
Hình 2.3. Đơn đồ thị có trọng số
Ta sẽ tìm độ dài của đường đi ngắn nhất từ a tới các đỉnh kế tiếp cho tới khi
đạt tới đỉnh kế tiếp cho tới khi đạt tới đỉnh z
Chỉ có hai đỉnh b và d liên thuộc với a nên chỉ có hai đường đi xuất phát từ
a là a,b và a,d với các độ dài tương ứng là 4 và 2. Do đó d là đỉnh gần a nhất.
Bây giờ tìm đỉnh tiếp theo gần a nhất trong các đường đi qua a và d ( cho
đến khi đạt đến đỉnh cuối cùng). Đường đi như thế ngắn nhất tới b là a,b với
độ dài 4 và đường đi như thế ngắn nhất tới e là a,d,e, độ dài 5. Do vậy đỉnh
tiếp theo gần a nhất là b.
Để tìm đỉnh thứ ba gần a nhất, ta chỉ xét các đường đi qua a, d và b (cho
đến khi đạt tới đỉnh cuối cùng). Đó là đường đi a,b,c độ dài 7 và đường đi
a,d,e,z độ dài 6. Vậy z là đỉnh tiếp theo gần a nhất và độ dài của đường đi
ngắn nhất từ a tới z là 6
Ví dụ 2.2 minh họa những nguyên tắc chung dung trong thuật toán
Dijkstra. Đường đi ngắn nhất từ đỉnh a tới z có thể tìm được bằng cách kiểm
tra trực tiếp. Nhưng cách làm này là hoàn toàn không dùng được cho cả người
và máy khi đồ thị có nhiều cạnh.
Tư tưởng
Bây giờ sẽ nghiên cứu bài toán tìm độ dài của đường đi ngắn nhất giữa a và
z trong đơn đồ thị liên thông, vô hướng có trọng số. Thuật toán Dijsktra được
thực hiện bằng cách tìm độ dài của đường đi ngắn nhất từ a tới đỉnh thứ hai…
cho tới khi tìm được độ dài của đường đi ngắn nhất từ đỉnh a tới đỉnh z.
Thuật toán dựa trên một dãy các bước lặp. Một tập đặc biệt các đỉnh được
xây dựng bằng cách cộng thêm một đỉnh trong mỗi bước lặp. Thủ tục gán
nhãn được thực hiện trong mỗi lần lặp đó. Trong thủ tục gán nhãn này, đỉnh w
được gán nhãn bằng độ dài đường đi ngắn nhất từ a tới w và chỉ đi qua các
đỉnh đã thuộc tập đặc biệt. Một đỉnh được thêm vào tập này là đỉnh có nhãn
nhỏ nhất so với các đỉnh chưa có trong tập đó.
Bây giờ sẽ đưa ra chi tiết của thuật toán Dijsktra. Đầu tiên, gán cho đỉnh a
nhãn bằng 0 và các đỉnh khác là ∞. Ta kí hiệu L0(a)=0 và L0(v) = ∞ cho tất cả
các nhãn ( bước lặp thứ 0). Các nhãn này là độ dài đường đi ngắn nhất từ đỉnh
a tới các đỉnh này, trong đó đường đi này chỉ chứa đỉnh a.( Vì không có
đường đi từ a tới các đỉnh khác a nên ∞ là độ dài đường đi ngắn nhất giữa a
và các đỉnh này).
Theo thuật toán Dijsktra sẽ xây dựng tập đặc biệt các đỉnh. Gọi Sk là tập
này sau bước lặp thứ k của thủ tục gán nhãn. Chúng ta bắt đầu từ S0= . Tập
Sk được tạo thành từ Sk-1 bằng cách thêm vào đỉnh u không thuộc Sk-1 có nhãn

28

nhỏ nhất. Khi đỉnh u được gộp vào Sk ta sửa đổi nhãn của các đỉnh không
thuộc Sk sao cho Lk(v), nhãn của v tại bước k, là độ dài của đường đi ngắn nhất
từ a tới v mà đường đi này chỉ chứa các đỉnh thuộc Sk ( tức là các đỉnh đã
thuộc tập đặc biệt các đỉnh cùng với u).
Giả sử v là một đỉnh không thuộc Sk . Để sửa nhãn của v ta thấy Lk(v) là độ
dài của đường đi ngắn nhất từ a tới v và chỉ chứa các đỉnh thuộc Sk. Để sửa
đổi nhãn, lưu ý rằng đường đi ngắn nhất từ a tới v chỉ chứa phần tử của Sk
hoặc là đường đi ngắn nhất từ a tới v chỉ chứa các phần tử của Sk-1 hoặc là
đường đi ngắn nhất từ a tới u ở bước k-1 cùng với cạnh (u,v). Nói cách khác
ta có:
Lk(a,v) = min {Lk-1(a,v), Lk-1(a,u) + w(u,v)} .
Thủ tục này được lặp bằng cách liên tiếp thêm các đỉnh vào tập đặc biệt các
đỉnh cho tới khi đạt tới đỉnh z. Khi thêm z vào tập đặc biệt các đỉnh thì nhãn
của nó bằng độ dài của đường đi ngắn nhất từ a tới z.
Thuật toán Dijsktra[2]
procedure Dijsktra (G: đơn đồ thị liên thông có trọng số, với trọng số
dương)
{ G có các đỉnh a = v0, v1, …, vn= z và trọng số w(vi,vj), với w(vi,vj) = ∞
nếu { vi, vj } không là một cạnh trong G}
for i:= 1 to n
L(vi) := ∞
L(a):= 0
S:=
{Ban đầu các nhãn được khởi tạo sao cho nhãn của a bằng 0, các đỉnh
khác = ∞, tập S là rỗng}
while z S
begin
u:= đỉnh không thuộc S có nhãn L(u) nhỏ nhất.
S:= S {u}
for tất cả các đỉnh v không thuộc S
if L(u) + w(u,v) < L(v) then L(v) := L(u) + w(u,v)
{thêm vào S đỉnh có nhãn nhỏ nhất và sửa đổi nhãn của các đỉnh
không thuộc S}
end { L(z) = độ dài đường đi ngắn nhất từ a tới z }
Độ phức tạp. [7]
Thuật toán Dijkstra tìm được đường đi ngắn nhất trên đồ thị sau thời gian
cỡ O(n2 ).
Ví dụ 2.3. Dùng thuật toán Dijsktra hãy tìm độ dài đường đi ngắn nhất giữa
hai đỉnh a và z của đồ thị có trọng số trên hình 2.4(a)
Các bước dùng thuật toán Dijsktra tìm độ dài của đường đi ngắn nhất giữa
hai đỉnh a và z được biểu diễn trên hình 2.4. Tại mỗi bước lặp của thuật toán
các đỉnh của tập S được khoanh tròn. Đường đi ngắn nhất chỉ chứa các đỉnh

29

đã thuộc Sk từ a tới mỗi đỉnh được in ra cho mỗi bước lặp. Thuật toán kết thúc
khi z được khoanh tròn. Ta nhận được đường đi ngắn nhất từ a tới z là a ,c, b,
d, e, z với độ dài bằng 13( hình 2.4 (g))
a
0
4
1
6
c

b

8
10
e

d

5
2
z
3
2


(a)
a
0
4
1
6

30

c
2(a)
b
4(a)
8
10
e

d

5
2
z
3
2


(b)
a
0
4
1
6
c
2(a)
b
3(a,c)
8
10

31

e
12(a,c)
d
10(a,c)
5
2
z
3
2


(c)
a
0
4
1
6
c
2(a)
b
3(a,c)
8
10
e
12(a,c)
d
8(a,c,b)
5

32

2
z
3
2


(d)
a
0
4
1
6
c
2(a)
b
3(a,c)
8
10
e
10(a,c,b,d)
d
8(a,c,b)
5
2
z
3
2
14(a,c,b,d)

33

(e)
a
0
4
1
6
c
2(a)
b
3(a,c)
8
10
e
10(a,c,b,d)
d
8(a,c,b)
5
2
z
3
2
13(a,c,b,d,e)

(f)
a
0
4
1
6
c