Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 15 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
15
Dung lượng
146 KB
Nội dung
MỘT SỐ ỨNG DỤNG THUẬT TOÁN DIJKSTRA Bùi Thu Hiền – THPT Chuyên Thái Bình LỜI MỞ ĐẦU Lý thuyết đồ thị phần quan trọng nội dung chương trình chun mơn Tin học trường chun Hầu đề thi học sinh giỏi có tốn liên quan đến lý thuyết đồ thị, để học sinh có kết cao cần trang bị cho em tảng tốt kỹ thuật cài đặt toán lý thuyết đồ thị Trong tham luận tơi xin đề cập đến Một số ứng dụng thuật tốn Dijkstra - tìm đường ngắn đỉnh s với tất đỉnh đồ thị có trọng số khơng âm THUẬT TỐN DIJKSTRA Bài tốn: Cho G = (V, E) đơn đồ thị có hướng gồm n đỉnh m cung, trọng số cung không âm Yêu cầu tìm đường ngắn từ đỉnh xuất phát s ∈ V đến đỉnh đích f ∈ V Thuật tốn Dijkstra (E.Dijkstra - 1959) mơ tả sau: Bước 1: Khởi tạo Với đỉnh v ∈ V, gọi nhãn d[v] độ dài đường ngắn từ s tới v Ban đầu d[v] khởi gán thuật toán Ford-Bellman (d[s] = d[v] = +∞ với ∀v ≠ s) Nhãn đỉnh có hai trạng thái tự hay cố định, nhãn tự có nghĩa tối ưu nhãn cố định tức d[v] độ dài đường ngắn từ s tới v nên tối ưu thêm Để làm điều ta sử dụng kỹ thuật đánh dấu: Free[v] = TRUE hay FALSE tuỳ theo d[v] tự hay cố định Ban đầu nhãn tự Bước 2: Lặp Bước lặp gồm có hai thao tác: - Cố định nhãn: Chọn đỉnh có nhãn tự do, lấy đỉnh u đỉnh có d[u] nhỏ nhất, cố định nhãn đỉnh u - Sửa nhãn: Dùng đỉnh u, xét tất đỉnh v sửa lại d[v] theo công thức: d [v] := min(d [v] , d [u] + c [u,v ]) Bước lặp kết thúc mà đỉnh đích f cố định nhãn (tìm đường ngắn từ s tới f); thao tác cố định nhãn, tất đỉnh tự có nhãn +∞ (khơng tồn đường đi) Có thể đặt câu hỏi, thao tác 1, đỉnh u cố định nhãn, giả sử d[u] tối ưu thêm tất phải có đỉnh t mang nhãn tự cho d[u] > d[t] + c[t, u] Do trọng số c[t, u] không âm nên d[u] > d[t], trái với cách chọn d[u] nhỏ Tất nhiên lần lặp s đỉnh cố định nhãn d[s] = Bước 3: Kết hợp với việc lưu vết đường bước sửa nhãn, thơng báo đường ngắn tìm cho biết không tồn đường (d[f] = +∞) for (∀v ∈ V) d[v] := +∞; d[s] := 0; repeat u := arg min(d[v]|∀v ∈ V); {Lấy u đỉnh có nhãn d[u] nhỏ nhất} if (u = f) or (d[u] = +∞) then Break; {Hoặc tìm đường ngắn từ s tới f, kết luận khơng có đường} for (∀v ∈ V: (u, v) ∈ E) {Dùng u tối ưu nhãn đỉnh v kề với u} d[v] := (d[v], d[u] + c[u, v]); until False; Chú ý: Nếu đồ thị thưa (có nhiều đỉnh, cạnh) ta sử dụng danh sách kề kèm trọng số để biểu diễn đồ thị, nhiên tốc độ thuật toán Dijkstra chậm trường hợp xấu nhất, cần n lần cố định nhãn lần tìm đỉnh để cố định nhãn đoạn chương trình với độ phức tạp O(n) Để thuật toán làm việc hiệu hơn, người ta thường sử dụng cấu trúc liệu Heap để lưu đỉnh chưa cố định nhãn Bài tập Bài 1: Ông Ngâu bà Ngâu Hẳn bạn biết ngày "ông Ngâu bà Ngâu" hàng năm, ngày đầy mưa nước mắt Tuy nhiên, ngày trước đó, nhà Trời cho phép "ơng bà" đồn tụ Trong vũ trụ vùng thiên hà nơi ơng Ngâu bà Ngâu ngự trị có N hành tinh đánh số từ đến N, ông hành tinh Adam (có số hiệu S) bà hành tinh Eva (có số hiệu T) Họ cần tìm đến gặp N hành tinh nối với hệ thống cầu vồng Hai hành tinh khơng có cầu vồng (hai chiều) nối chúng Họ tới mục tiêu theo đường ngắn Họ với tốc độ không đổi nhanh tốc độ ánh sáng Điểm gặp mặt họ hành tinh thứ u cầu: Hãy tìm hành tinh cho ông Ngâu bà Ngâu đến lúc thời gian đến sớm Biết rằng, hai người qua hành tinh họ đến hành tinh vào thời điểm khác Dữ liệu: vào từ file văn ONGBANGAU.INP: - Dòng đầu số N, M, S, T (N ≤ 100, ≤ S ≠ T ≤ N), M số cầu vồng - M dòng tiếp, dòng gồm ba số nguyên I, J, L thể có cầu vồng nối hai hành tinh i J có độ dài L (1 ≤ I ≠ J ≤ N, < L ≤ 200) Kết quả: ghi file văn ONGBANGAU.OUT: tính chất cầu vồng, năm khác, nên không tồn hành tinh thoả mãn yêu cầu ghi dòng chữ CRY Nếu có nhiều hành tinh thoả mãn ghi hành tinh có số nhỏ Ví dụ: ONGBANGAU.INP 4 4 ONGBANGAU.OUT Thuật tốn: Ta có nhận xét: + Hai hành tinh nối đến nhiều cầu vồng + Ông Ngâu bà Ngâu tới mục tiêu theo đường ngắn + Họ với vận tốc không đổi nhanh vận tốc ánh sáng Thực chất tốn đồ thị, ta có thuật tốn sau: Từ hành tinh S (nơi ông Ngâu ở) ta xây dựng bảng SP, SP[i] đường ngắn từ hành tinh S đến hành tinh i (do ông Ngâu tới mục tiêu theo đường ngắn nhất) SP[i] = tức đường từ hành tinh S đến hành tinh i Tương tự ta xây dựng bảng TP, TP[i] đường ngắn từ hành tinh T đến hành tinh i Và TP[i] = tức khơng có đường từ hành tinh T đến hành tinh i Do yêu cầu toán tìm hành tinh khác S T mà ơng bà Ngâu đến lúc thời gian nhanh Tức ta tìm hành tinh h cho (h khác S T) và(SP[h] = ST[h] ) đạt giá trị nhỏ khác Nếu hành tinh h thoả mãn ta thơng báo CRY Để xây dựng mảng SP ST ta chọn giải thuật Dijkstra tìm đường ngắn đỉnh đồ thị Bài 2: Đôi bạn Trước Tuấn Mai hai bạn lớp hai bạn học khác trường Cứ sáng, hai từ nhà tới trường theo đường thời gian (có thể có nhiều đường thời gian nhất) Nhưng hơm nay, hai bạn muốn gặp để bàn việc họp lớp cũ nhân ngày 20-11 Cho biết sơ đồ giao thông thành phố gồm N nút giao thông đánh số từ đến N M tuyến đường phố (mỗi đường phố nối nút giao thơng) Vị trí nhà Mai Tuấn trường hai bạn nằm nút giao thông Cần xác định xem Mai Tuấn có cách thoả mãn yêu cầu nêu trên, đồng thời họ lại gặp nút giao thơng đường tới trường hay khơng ? (Ta nói Tuấn Mai gặp nút giao thơng họ đến nút giao thơng thời điểm) Nếu có nhiều phương án phương án để Mai Tuấn gặp sớm Dữ liệu: vào từ file văn FRIEND.INP - Dòng chứa số nguyên dương N, M (1 ≤ N ≤ 100); - Dòng chứa số nguyên dương Ha, Sa, Hb, Sb số hiệu nút giao thông tương ứng với: Nhà Tuấn, trường Tuấn, nhà Mai, trường Mai - Dòng thứ i số M dòng chứa số nguyên dương A, B, T Trong A B hai đầu tuyến đường phố i Còn T thời gian (tính giây ≤ 1000) cần thiết để Tuấn (hoặc Mai) từ A đến B từ B đến A Giả thiết sơ đồ giao thông thành phố đảm bảo để từ nút giao thông đến tất nút lại Kết : ghi file văn FRIEND.OUT - Dòng 1: Ghi từ YES hay NO tuỳ theo có phương án giúp cho hai bạn gặp hay khơng Trong trường hợp có phương án: - Dòng 2: Ghi thời gian để Tuấn tới trường - Dòng 3: Ghi nút giao thơng theo thứ tự Tuấn qua - Dòng 4: Ghi thời gian để Mai tới trường - Dòng 5: Ghi nút giao thông theo thứ tự Mai qua - Dòng 6: Ghi số hiệu nút giao thơng mà hai bạn gặp - Dòng 7: Thời gian sớm tính giây kể từ sáng mà hai bạn gặp Ví dụ : Với sơ đồ giao thông sau: (N=6,M=7, Ha=1, Sa=6, Hb=2, Sb=5) Dòng FRIEND.INP 10 10 5 15 20 15 FRIEND.OUT YES 25 30 10 10 10 15 5 20 15 Thuật toán: Sử dụng thuật toán Dijkstra, xây dựng thủ tục: Dijkstra(start:intger, var d: mảng_nhãn); để xây dựng mảng nhãn d cho đường ngắn từ điểm xuất phát start đến đỉnh (có thể tới từ xuất phát) Sau gọi thủ tục lần lời gọi: Dijkstra(ha,d1); d1 cho biết đường ngắn xuất phát từ nhà Tuấn Dijkstra(sa,d2); d2 cho biết đường ngắn xuất phát từ nhà Mai Dijkstra(hb,d3); d3 cho biết đường ngắn xuất phát từ trường Tuấn Dijkstra(sb,d4); d4cho biết đường ngắn xuất phát từ trường Mai Điểm hẹn nút u cần thỏa mãn điều kiện sau: d1[u] + d3[u]=d1[sa] {thời gian Tuấn từ nhà tới điểm hẹn + Tuấn} d2[u] + d4[u] = d2[sb] {thời gian Mai từ nhà tới điểm hẹn + từ điểm hẹn tới trường Mai} d1[u] = d2[u] {thời gian từ nhà tới điểm hẹn Tuấn Mai nhau} d1[u] nhỏ {thời gian Tuấn từ nhà tới điểm hẹn sớm nhất} Để ghi kết vào file FRIENDS.OUT, cần gọi thủ tục Dijkstra lần nữa: Dijkstra(u,d); mảng d(N) cho biết nhãn đường ngắn Bài 3: Đường giới hạn Một mạng giao thông gồm N nút giao thông đánh số từ đến N Với cặp nút i, j có đường hai chiều đoạn đường đó, người ta quy định chiều cao ngun khơng âm c[i,j] không lớn 6000 chiều cao tối đa cho xe đoạn đường (c[i,j]=0 có nghĩa khơng có đường từ i đến j) Cho hai nút s t Hãy tìm hành trình từ s đến t qua nút khác cho chiều cao cho phép tối đa với xe chạy hành trình lớn Dữ liệu: vào từ file văn HIGHT.INP : - Dòng thứ ghi số N, s, t (N0 số dòng tiếp theo, dòng ghi đỉnh hành trình từ s đến t với chiều cao tối đa cho phép h ∞ Thuật toán: Gọi H[i] chiều cao lớn xe để từ s đến i Khởi tạo gán H[s]:=+∞ H[i] =0 với i ≠ s Thuật toán sửa nhãn tương tự thuật toán Dijkstra Repeat u:=0; max:=0; for v:=1 to n if free[v] and (h[v] > max) then begin max:=h[v]; u:=v; end; if u=0 then break; free[u]:=false; for v:=1 to n if a[u,v] then if h[v] < min(h[u],c[u,v]) then begin h[v]:=min(h[u],c[u,v]); trace[v]:=u; end; until false; Bài 4: Tổng số đường ngắn Cho đồ thị vô hướng G gồm N đỉnh, M cạnh, cạnh có trọng số nguyên dương, hai đỉnh có khơng q cạnh nối Cho trước hai đỉnh s t, tính số đường ngắn từ s đến t Hai đường khác thứ tự đỉnh đường khác Thuật toán: Kết hợp Dijkstra với quy hoạch động - Theo thuật toán Dijkstra gọi d[i] độ dài đường ngắn từ đỉnh s đến đỉnh i Khởi tạo d[i]=+∞ với i ≠ s d[s]=0 - Quy hoạch động gọi f[i] số đường ngắn từ đỉnh s đến đỉnh i Khởi tạo f[i]=0 với i ≠ s f[s]=1 Trong chương trình Dijkstra: - Mỗi tìm đường có độ dài ngắn (d[v]>d[u]+c[u,v]) ta tiến hành thay đổi d[v]:=d[u]+c[u,v]) đồng thời f[v]:=f[u] - Mỗi tìm đường có độ dài (d[v]=d[u]+c[u,v]) ta thay đổi f[v]:=f[v]+f[u] Kết cần tìm f[t] Đoạn chương trình Dijkstra kết hợp quy hoạch động For v:=1 to n d[v]:=maxlongint; d[s]:=0; For v:=1 to n f[v]:=0; f[s]:=1; Fillchar(Free,sizeof(Free),true); Repeat U:=0; mi:=maxlongint; For v:=1 to n If (Free[v]) and (d[v]d[u] + c[u,v] then Begin d[v]:=d[u] + c[u,v]; f[v]:=f[u]; end Else if d[v] = d[u] + c[u,v] then f[v]:=f[v] + f[u]; Until false; Sử dụng Dijkstra để đặt cận cho số toán duyệt Bài 5: ROADS N thành phố đánh số từ đến N nối với đường chiều Mỗi đường có hai giá trị: độ dài chi phí phải trả để qua Bob thành phố Bạn giúp Bob tìm đường ngắn đến thành phố N, biết Bob có số tiền có hạn K mà thơi Dữ liệu: vào từ file văn ROADS.INP Dòng ghi t số test Với test: - Dòng đầu ghi số nguyên K (0 ≤ K ≤ 10000) số tiền tối đa mà Bob chi cho lệ phí đường - Dòng ghi số nguyên N (2 ≤ N ≤ 100) số thành phố - Dòng ghi số nguyên R (1 ≤ R ≤ 10000) số đường nối - Mỗi dòng N dòng sau ghi số nguyên S, D, L, T mô tả đường nối S D với độ dài L (1 ≤ L ≤ 100) chi phí T (0 ≤ T ≤ 100) Kết quả: ghi file văn ROADS.OUT Với test, in độ dài đường ngắn từ đến N mà tổng chi phí khơng q K Nếu khơng tồn tại, in -1 Ví dụ: ROADS.INP 4 1 ROADS.OUT 11 -1 4 4 2 3 1 4 1 Thuật toán: Sử dụng thuật tốn Dijkstra: - Lần 1: tìm đường ngắn (về khoảng cách) ngược từ đỉnh N đỉnh khác để tạo mảng mindist - Lần 2: tìm đường ngắn (về chi phí tiền) ngược từ đỉnh N đỉnh khác để tạo mảng mincost Hai mảng mindist mincost dùng làm cận cho trình duyệt sau: Thực duyệt theo đỉnh từ đỉnh Giả sử duyệt tới đỉnh i, quãng đường d số tiền tiêu t Ngay đầu thủ tục Duyet(i,d,t) đặt cận: Nếu (d+mindist[i]>= đường phương án tốt nhất) khơng cần duyệt tiếp phương án thời Nếu (t+mincost[i]>số tiền có Bob k) không cần duyệt tiếp phương án thời Trong chương trình gọi thủ tục Duyet(1,0,0) Chú ý: Để q trình tìm đỉnh duyệt nhanh chóng ta cần tổ chức danh sách kề Chương trình tham khảo: const fi fo maxn infinity maxtime = = = = = type pt tnode = ^tnode; = record v : byte; l, t : byte; next : pt; end; = array[1 maxn] of word; = array[1 maxn, maxn] of word; m1 m2 var list dd cost, dist mincost, mindist k n best f,g t,test: longint; : : : : : : : 'ROADS.INP'; 'ROADS.OUT'; 100; 20000; 180; array[1 maxn] of pt; array[1 maxn] of b∞lean; m2; m1; word; byte; word; : text; procedure init; var i, r, u, v, l, t : word; tmp : pt; begin readln(f, k); readln(f, n); readln(f, r); for u:=1 to n for v:=1 to begin cost[u, dist[u, end; {so tien cua Bob} {so pho} {so duong} {khoi tri nhan gia tien , nhan khoang cach} n v]:=infinity; v]:=infinity; {to chuc cac danh sach lien ket chieu cua cac duong Moi danh sach list[i] cho biet cac co duong truc tiep tu i sang} for i:=1 to n {khoi tri cac nut goc cua cac danh sach lien ket list[i]} list[i]:=nil; for i:=1 to r begin readln(f, u, v, l, t); new(tmp); tmp^.v:=v; tmp^.l:=l; tmp^.t:=t; tmp^.next:=list[u]; list[u]:=tmp; {so gian lai du lieu} if l < dist[u, v] then dist[u, v]:=l; if t < cost[u, v] then cost[u, v]:=t; end; end; procedure dijkstra(var a : m2; var dist : m1); {Thuat toan dijkstra tim khoang cach ngan nhat tu i toi N} var chua : array[1 maxn] of b∞lean; : word; i, j, last : byte; begin fillchar(chua, sizeof(chua), true); {mang danh dau da xet} for i:=1 to n dist[i]:=infinity; {khoi tri mang nhan} dist[n]:=0; {nhan cua dinh N} chua[n]:=false; {danh dau da xet N} last:=n; {last: dinh chua xet co nhan nho nhat} for i:=2 to n {n-1 lan sua nhan thi xong} begin {sua nhan cho cac dinh j chua xet dua vao nhan cua last} for j:=1 to n if chua[j] and (a[j, last] + dist[last] < dist[j]) then dist[j]:=dist[last] + a[j, last]; {tim dinh chua xet o nhan nho nhat} min:=infinity+1; for j:=1 to n if chua[j] and (dist[j] < min) then begin min:=dist[j]; last:=j; end; {danh dau da xet xong dinh last} chua[last]:=false; end; end; procedure try(last : byte; l, t : word); {Duyet tiep Bob da toi last, da di doan duong l, da tieu t xu} var tmp : pt; begin if (l + mindist[last] >= best) {dk can ve duong di} or (t + mincost[last] > k) then {dk can ve tien} exit; if last = n then {ve toi dich: Diem dung de quy} begin best:=l; exit; end; end; tmp:=list[last]; {tmp: last} while tmp nil {duyet chon cac de cu cho tiep theo last} begin if not dd[tmp^.v] then {thanh v chua qua} begin dd[tmp^.v]:=true; {danh dau da qua v} try(tmp^.v, l+tmp^.l, t+tmp^.t); {di tiep tu v} dd[tmp^.v]:=false; {quay lui} end; tmp:=tmp^.next; {de cu khac} end; procedure process; begin {xay dung cac mang cost va dist de lam can phuc vu duyet de quy} dijkstra(cost, mincost); dijkstra(dist, mindist); {khoi tri} best:=infinity; fillchar(dd, sizeof(dd), false); try(1, 0, 0); {duyet tu (duong da di =0, tien da tieu=0} end; procedure done; begin if best = infinity then writeln(g, -1) else writeln(g, best); end; BEGIN assign(f, fi); reset(f); assign(g, fo); rewrite(g); readln(f,test); for t:=1 to test begin init; process; done; end; close(f); close(g); END Bài 6: Du lịch Cho N thành phố đánh số từ đến N Một người muốn du lịch từ thành phố A đến thành phố B sau quay lại A Người muốn đường từ B A không quay lại thành phố qua đường từ A đến B Hãy tìm cho người hành trình với chi phí Dữ liệu: vào từ file văn TOURIST.INP - Dòng thứ ghi số nguyên dương N, A, B (N