Chương 6: Các thuật tốn tìm kiếm trên đồ thị
} void mainvoid{
Lien_Thong; }
6.4. TÌM ĐƯỜNG ĐI GIỮA HAI ĐỈNH BẤT KỲ CỦA ĐỒ THỊ
Bài toán: Cho đồ thị G=V, E. Trong đó V là tập đỉnh, E là tập cạnh của đồ thị. Hãy tìm
đường đi từ đỉnh s
∈
V tới đỉnh t
∈
V. Thủ tục BFSs hoặc DFSs cho phép ta duyệt các đỉnh cùng một thành phần liên thông với
s. Như vậy, nếu trong số các đỉnh liên thông với s chứa t thì chắc chắn có đường đi từ s đến t. Nếu trong số các đỉnh liên thông với s khơng chứa t thì khơng tồn tại đường đi từ s đến t. Do vậy,
chúng ta chỉ cần gọi tới thủ tục DFSs hoặc BFSs và kiểm tra xem đỉnh t có thuộc thành phần liên thơng với s hay không. Điều này được thực hiện đơn giản thông qua mảng trạng thái
chuaxet[]. Nếu chuaxet[t] = False thì có nghĩa t cùng thành phần liên thơng với s. Ngược lại chuaxet[t] = True thì t khơng cùng thành phần liên thông với s.
Để ghi nhận đường đi từ s đến t, ta sử dụng một mảng truoc[] thiết lập giá trị ban đầu là 0. Trong quá trình duyệt, ta thay thế giá trị của truoc[v] để ghi nhận đỉnh đi trước đỉnh v trong đường
đi tìm kiếm từ s đến v. Khi đó, trong thủ tục DFSv ta chỉ cần thay đổi lại như sau:
void DFS int v{ chuaxet[v]:=
FALSE; for u
∈kev { if chuaxet[u] {
truoc[u]=v; DFSu;
} }
}
Đối với thủ tục BFSv được thay đổi lại như sau:
void BFSint u{ queue
= φ;
u = queue; nạp u vào hàng đợi chuaxet[u]
= false;
đổi trạng thái của u while
queue ≠ φ { duyệt tới khi nào hàng đợi rỗng
queue=p; lấy p ra từ khỏi hàng đợi
131
Chương 6: Các thuật tốn tìm kiếm trên đồ thị
for v
∈ kep { đưa các đỉnh v kề với p nhưng chưa được xét vào hàng đợi if chuaxet[v] {
v= queue;
đưa v vào hàng đợi chuaxet[v]
= false;
đổi trạng thái của v truoc[v]=p;
} }
} end while } end BFS
Kết quả đường đi được đọc ngược lại thông qua thủ tục Result như sau:
void Resultvoid{ iftruoc[t]==0{
Khơng có
đường đi từs đến t; return;
} j = t;
whiletruoc[j]=s{ thăm đỉnh j;
j=truoc[j]; }
thăm đỉnh s; }
Ví dụ. Tìm đường đi từ đỉnh 1 đến đỉnh 7 bằng thuật tốn tìm kiếm theo chiều rộng với đồ
thị trong hình 6.4 dưới đây 2 6
8 7
1 4 5
10 3
11 9
13 12
Hình 6.4. Đồ thị vơ hướng G=V,E
132
Chương 6: Các thuật tốn tìm kiếm trên đồ thị Ta có, BFS1 = 1,2,3,11,4,6,12,13,7,8,9,10,5. Rõ ràng chuaxet[7] = True nên có đường đi
từ đỉnh 1 đến đỉnh 7. Bây giờ ta xác định giá trị trong mảng truoc[] để có kết quả đường đi đọc theo chiều ngược lại.
Truoc[7] = 6; truoc[6] = 2; truoc[2] =1 = đường đi từ đỉnh 1 đến đỉnh 7 là 1 =2=6=7.
Tồn văn chương trình được thể hiện như sau:
include stdio.h include conio.h
include io.h include stdlib.h
include dos.h define MAX
100 define TRUE
1 define FALSE
0int n, truoc[MAX], chuaxet[MAX], queue[MAX]; int A[MAX][MAX]; int s, t;
Breadth First Search void Initvoid{
FILE fp; int i, j; fp=fopenlienth.IN,
r; iffp==NULL{
printf\\n Khong co file input; delay2000;return;
} fscanffp,d,
n; printf\\n So dinh do thi:d,n;
printf\\n Ma tran ke cua do thi:; fori=1;
i=n;i++{ printf\\n;
forj=1; j=n;j++{
fscanffp,d, A[i][j];
printf3d, A[i][j];
}
133
Chương 6: Các thuật toán tìm kiếm trên đồ thị
} fori=1;
i=n;i++{ chuaxet[i]=TRUE;
truoc[i]=0; }
} void Resultvoid{
printf\\n\\n; iftruoc[t]==0{
printf\\n Khong co duong di tu d den d,s,t; getch;
return; }
printf\\n Duong di tu d den d la:,s,t; int j = t;printfd=, t;
whiletruoc[j]=s{ printf3d=,truoc[j];
j=truoc[j]; }
printf3d,s; }
void Invoid{ printf\\n\\n;
forint i=1; i=n; i++ printf3d,
truoc[i]; }
void BFSint s { int dauQ, cuoiQ, p, u;printf\\n;
dauQ=1;cuoiQ=1; queue[dauQ]=s;chuaxet[s]=FALSE;
while dauQ=cuoiQ{
u=queue[dauQ]; dauQ=dauQ+1;
printf3d,u;
134
Chương 6: Các thuật tốn tìm kiếm trên đồ thị
for p=1; p=n;p++{ ifA[u][p]
chuaxet[p]{ cuoiQ=cuoiQ+1;queue[cuoiQ]=p;
chuaxet[p]=FALSE;truoc[p]=u; }
} }
} void duongdivoid{
int chuaxet[MAX], truoc[MAX], queue[MAX]; Init;BFSs;Result;
} void mainvoid{
clrscr; printf\\n Dinh dau:; scanfd,s;
printf\\n Dinh cuoi:; scanfd,t; Init;printf\\n;BFSs;
n;getch; Result;getch;
}
6.5. ĐƯỜNG ĐI VÀ CHU TRÌNH EULER