Thuật toán quay lui và ứng dụng
Lã Văn Chinh
Giả thiết một cấu hình cần tìmđược mô tả bởi một bộ phận gồm n thành phần a1, a2,... an.
Giả sử tìm được i -1 thành phần a1, a2, ai-1, ta tìm thành phần thứ i bằng cách duyệt tất cả
cáckhả năng có thể của ai. Với mỗi khả năngj kiểm tra xem nó có chấp nhận được không.
Xảy ra hai trường hợp.
Nếu j chấo nhận được thì xác định ai theo j và kiểm tra xem i = n chưa, nếu i = n thì ta ghi
nhận một cấu hình, còn nếu i < nta gọi tiến hành xác định ai+1
Nếu thử tất cả các khả năng mà không có khả năng nào chấp nhận được thì quay lại bước
trước xác định lại ai-1
Nội dung của thuật toán này rấtphù hợp với việc gọi đệ quy. Ta có thủ tục đệ quy sau đây:
Procedure Try (i:tinteger);
Var j:integer;
Begin
For j:=1 to n do
if chấp nhận j then
begin
xác nhận aj theo j
if i=n then < ghi nhận cấu hình >
else try(i+1);
end;
end;
Để minh hoạ cho thuật toán này ta áp dụng giải bài toán xếp hậu:
Nội dung bài toán: Liệt kê tất cả các cách sắp xếp những con hậu trên bàn cờ NxN sao
cho chúng không ăn được nhau.
Giải: Ta xếp n con hậu trên n dòng, heo nguyên lý nhân ta có n
n
cách sắp xếp thoả mãn
điều kiện đầu bải. Để làm điều đó ta dùng thủ tục đệ quy mô tả ở trên để giải.Ta đánh ghi
số cột và dòng của bàn cờ từ 1 đến n, mỗi cách sắp xếp ứng với 1 bộ gồm a
1
,a
2
,.....,a
n
vớia
i
= j (j=1,2,...,n) có nghĩa là con hậu thứ i đặt vào cột j. Giả sử ta chọn được i-1 con hậu
bằng cách duyệt tất cả các khả năng của nó.
Quan trọng nhất là ta tìm điều kiện chấp nhận j, một con hậu đứng ở một ô trong bàn cờ nó
có nhiều nhất bốn hướng đi (đường dọc, đường ngang và hai đường chéo).
Vậy điều kiện chấp nhận thứ i thoả mãn không nằm trên đường đi của tất cả i-1 con hậu đã
xếp.Bởi vì n con hậu xếp ở hàng nên đường đi ngang của chúng là không chiến nhau, do
đó khi chọn con hậu thư ichỉ cần kiểm tra xem trên 2 đường chéo và đường dọc của chúng
có chiếu vào những con hậu đã xếp không. Để kiểm tra điều này mỗi đường ta dùng một
biến trạng thái.
* Đường dọc kiểm soát bằng biến b[j],(j=1,2,...,n).
* Một đường chéo kiểm soát bằng biến c[i+j],i+j={2,....,2n}.
* Còn đường chéo kia kiểm soát bằng biến d[i-j],i-j={1-n,....,n-1}.
Các biến trạng thái này khởi gán giá trị True trong thủ tục Init. Như vậy con hậu thứ i
được chấpnhận xếp vào cột j nếu nó thoả mãn cả ba biến b[j],c[i+j],d[i-j] đều có giá trị
true.Các biến này gán giá trị False khi xếp xong con hậu thứ i, và trả lại giá trị true sau khi
gọi Resulthay Try(i+1). Ta có chương trình Pascal sau :
Program XepHau;
Uses crt;
var n : integer;
a:array[1..30] of integer;
b:array[1..30] of boolean;
c:array[2..60]of boolean;
count,d:word;
Procedure Init;
Var i:integer;
Begin
Write('Cho do rong ban co n= ');
Readln(n);
Count:=0; d:=0;
For i:=1 to n do b[i]:=true;
For i:=2 to 2*n do c[i]:=true;
For i:=1-n to n-1 do d[i]:=true;
End;
Procedure Result;
Var i:integer;
Begin
d:=d+1; count:=count+1;
Write('Cach xep thú,count:5,'.');
for i:=1 to n do write(a[i]:2);
Writeln;
if d = 24 then
begin
readln; d : = 0;
end;
end;
Procedure try(i:integer);
Var j : integer;
Begin
For j:=1 to n do
If (b[j]) and (c[i + j]) and (d[i- j]) then
Begin
a [i] : = j;
b [j] : = false; c[i + j]: = false;
d [i] : = false;
if i = n then Result else try(i+1);
b [j] : = true; c[i + j]: = true;d[i + j]: = true;
end;
end;
begin
clrscr;
Init;
Try(1);
Write ('An Enter de ket thuc:');
Readln;
End.
Để hiểu sâu sắc thuật toán này, mời bạn đọc làm một số bài toán sau:
Bài 1: Hãy viết chương trình liệt kê tất cả các dãy nhị phân có độ dài n.
Bài 2: Hãy viết chương trình liệt kê các hoán vị của {1,2,...,n}
Bài 3: Hãy viết chương trình liệt kê các tổ hợp chập m của {1,2,...,n}
Bài 4: Hãy viết chương trình liệt kê tất cả các chu trình Haminton của đồthị.
(Chu trình bắt đầu từ đỉnh v nào đó qua tất cả các đỉnh còn lại, mỗiđỉnh đúng một lần rồi
quay trở về đỉnh v được gọi là chu trình Hamilton).