Thuật toán Dịjkastra cải tiến
Trang 1Thuật toán Dijkstra với những cải tiến
duccanh841988@yahoo.com
Thuật toán Dijkstra là một thuật toán tốt và hiệu quả trong việc tìm đường đi ngắn nhất trên đồ thị trong trường hợp trọng số trên các cung không âm Quả thực thuật toán đã được đề cập rất nhiều, nhưng ở đây mình xin giới thiệu một cải tiến mới
Xét bài toán 1: cho đồ thị G=(V,E), hãy tìm đường đi ngắn nhất từ đỉnh xuất phát s tới đỉnh đích f
Lời giải: đây là một bài toán kinh điển của thuật toán Dijkstra:
d[i] = + ∞ với mọi I thuộc [1 n]
Đánh dấu đỉnh s
d[s]=0
repeat
{tìm 1 đỉnh k còn tự do và có trọng số d[k] nhỏ nhất.}
{nếu k=f thì dừng}
{Đánh dấu đỉnh k.}
với mọi đỉnh I còn tự do thì
d[i] = min {d[i], d[k] +l[k,i]}
until {mọi đỉnh đều bị đánh dấu };
Bây giờ ta sẽ xét ví dụ 1:
Cho 1 hình chữ nhật (NxM) mỗi ô là 1 số nguyên Từ 1 ô (x,y) hãy tìm đường đi đến ô (u,v) sao cho tổng các số ghi trên các ô là bé nhất (qui tắc đi: từ 1 ô chỉ đi được đến các ô
có chung cạnh)
Lời giải: Đây là 1 bài toán quen thuộc và có thể dùng nhiều cách giải khác nhau, nhưng ở đây chúng ta chỉ xét cách giải bằng thuật toán Dijkstra:
Gọi a[I,j] là trọng số nhỏ nhất khi đi từ ô (x,y) đến ô (I,j)
Khi đó cài đặt thuật toán trên ta sẽ tìm ra a[u,v] là trọng số nhỏ nhất của đường đi từ (x,y) đến (u,v)
Vấn đề ở đây là khi tìm 1 đỉnh k tự do và có trọng số nhỏ nhất ta phải dùng 1 vòng for lồng nhau để duyệt hết bảng, trong khi có rất nhiều ô tự do có giá trị bằng + ∞ và nhiều ô
đã được cố định nhãn.Vì vậy ở đây ta có thể cải tiến dùng thêm 2 mảng 1 chiều để giữ lại các ô tự do mà có giá trị a[I,j] + ∞ Việc này có ý nghĩa rất lớn trong việc tìm kiếm ô tự
do có giá trị min
Ví dụ: xuất phát từ ô (1,3) đến ô (3,1)
Trang 2Tổng chi phí của đường đi ngắn nhất là 20 Nếu dùng Dijkstra bình thường thì mỗi lần tìm min ta phải duyệt hết, nhưng nếu ta chỉ tìm những ô đang tự do mà có giá trị khác +
∞ thời gian chạy chương trình sẽ nhanh hơn
Quá trình chạy theo bảng sau:
Những ô màu xanh và đỏ là những ô thuộc vùng tìm kiếm giá trị min,ô màu đỏ là ô có giá trị min trong bước chạy đó, những ô màu đen là những ô không thuộc vùng tìm kiếm giá trị min Ta có thể thấy số ô trong vùng tìm kiếm (ô màu xanh và đỏ) ít hơn rất nhiều so với ô màu đen
Chương trình dưới đây dùng mảng q:array[1 2,1 1000] of byte để giữ lại chỉ số các ô trong vùng tìm kiếm
uses crt;
const fi='vd1.inp';
fo='vd1.out';
max=100;
maxa=10000;
interval=1193180/65536;
Trang 3dx:array[1 4]of integer=(1,0,-1,0); dy:array[1 4]of integer=(0,-1,0,1); var a,c:array[1 max,1 max]of word; trace:array[1 max,1 max]of byte; free:array[0 max,0 max]of boolean; q:array[1 2,1 2000]of byte;
m,n,x,y,u,v,top:integer;
sttime:longint;
time: longint absolute 0:$46c;
procedure nhap;
var f:text;
i,j:integer;
begin
assign(f,fi);
reset(f);
readln(f,n,m,x,y,u,v);
for i:=1 to n do
begin
for j:=1 to m do read(f,c[i,j]);
readln(f);
end;
close(f);
sttime:=time;
end;
procedure init;
var i,j:integer;
begin
fillchar(free,sizeof(free),false); for i:=1 to n do
for j:=1 to m do
begin
a[i,j]:=maxa;
free[i,j]:=true;
end;
end;
procedure ijk;
var i,j,min,k,h:integer;
begin
a[x,y]:=c[x,y];
top:=1;
q[1,top]:=x;
q[2,top]:=y;
repeat
Trang 4for i:=1 to top do
if(a[q[1,i],q[2,i]]a[k,h]+c[k+dx[i],h+dy[i]]) then begin
a[k+dx[i],h+dy[i]]:=a[k,h]+c[k+dx[i],h+dy[i]]; trace[k+dx[i],h+dy[i]]:=i;
inc(top);
q[1,top]:=k+dx[i];
q[2,top]:=h+dy[i];
end;
until false;
end;
procedure xuat;
var i,j,k:integer;
f:text;
begin
assign(f,fo);
rewrite(f);
writeln(f,a[u,v]);
i:=u;
j:=v;
while (ix)or(jy)do
begin
writeln(f,'(',i,',',j,')');
k:=trace[i,j];
i:=i-dx[k];
j:=j-dy[k];
end;
writeln(f,'(',i,',',j,')');
write(f,(time-sttime)/interval:1:10);
close(f);
end;
procedure test;
var i,j:integer;
f:text;
begin
assign(f,fi);
rewrite(f);
writeln(f,'100 100 1 1 100 100');
randomize;
for i:=1 to 100 do
begin
for j:=1 to 100 do
write(f,random(20),' ');
Trang 5end;
close(f);
end;
begin
nhap;
init;
ijk;
xuat;
end
Nếu ta tính thời gian chạy thì chương trình trên chạy bộ test cỡ n=100, m=100, x=1, y=1, u=100, v=100 thì nhanh hơn chương trình Dijkstra rất nhiều (theo mình thử thì mất khoảng 0,055s, trong khi Dijkstra bình thường mất khoảng 2 s) Đối với các bài toán có
áp dụng Dijkstra (như bài toán 1) thì ta có thể áp dụng cái tiến trên để chương trình có thể chạy nhanh hơn Các bạn hãy thử sức với bài toán sau:
Cho một ma trận nhị phân kích thước 200X200 Hai ô chứa số 0 và có chung cạnh gọi là
2 ô kề nhau Hai ô đến được nhau nếu chúng có thể đến được nhau qua 1 số ô kề nhau, số bước dịch chuyển là độ dài của đường đi.Từ 1 ô (x,y) hãy tìm đường đi ngắn nhất đến một ô bất kì nằm sát biên của ma trận