Tải bản đầy đủ (.doc) (5 trang)

Thuật toán quay lui

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (153.04 KB, 5 trang )

Khử quay lui và bài toán từ đẹp
Lương Công Hảo
Sau khi đọcxong bài "Một số bài toán tin khó" của thầy Nguyễn Xuân Huy và thầy Hồ Sỹ
Đàm và các số báo của TH&NT viết về Giải Thuật Quay lui và khử Quaylui của các tác
giả: Chu Đức Minh, Nguyễn Văn Trường, Nguyễn Duy Hàm,Trương Thị Thu Hường…đã
nói rất rõ về giải thuật này. Song đểvận dụng và giải quyết một số bài toán (như bài toán
"Từ đẹp"..) thì quả thật là khó khăn. Nhằmgiúp các em học sinh hiểu thấu đáo hơn, tôi xin
mạnh dạn đưa ra cácý kiến sau mong các em học sinh và các bạn đồng nghiệp tham khảo
gópý.
Xuất pháttừ bài toán: Viết tất cả các dãy có độ dài n từ các ký tự A,B, C(chẳng hạn với
n=3, ta có: AAA, AAB, AAC,..). Để giải quyết bài toánnày ta chỉ cần dùng 3 vòng lặp
For(n=3) là được. Song với n lớn thì sao? Nếu giải quyết bài toán nàybằng quay lui, ta có
thủ tục sau:
hellip;
m=’ABC’
hellip;
proceduretry(i:integer);
varj:integer;
begin
for j:=1 to3 do
begin
x[i]:=m[j];
if i=n theninkq esle try(i+1);
end;

try(1);

Từ mô hìnhtrên ta thấy:
- Khi gọi Try(i+1) mức tănglên 1.
- Khi lùi mức giảm 1
- Và giảm đến mức 1.


Như vậy,để tránh việc tràn Stack, ta có thể khử quay lui nếu biết tổ chức vàlưulại các giá
trị đề cử j. Để lưu j ứng với các mức của i ta dùngmảng a: array[1..n+1] of byte với a[i]=i
(tại mỗi thời điểm) (xuất pháta[i]=1, i=1..n). Sau mỗi lần đề cử được tăng j ta tăng
a[i]:=a[i]+1, nếua[i] vượt quá giá trị đề cử ta gán a[i]:=1. Việc thay đổi này cóthể đặt trong
hàm Đề cử (decu) hoặc trong thủ tục khử quay lui(khu_try).
Trong bàitoán này, hàm decu rất đơn gảin vì các khả năng của j đều chấp nhậnđược vì
không có ràng buộc nào. Để lưu nghiệm ta dùng biến string(nhược điểm: dài tối đa 256)
Sau đây là văn bản chươngtrình:
Programkhu_quay_lui;
Const n=3;
Varx,y:string ;
A:array[1..300] of byte;
I:integer;
Procedureinkq;
Begin
Writeln(x);
End;
Functiondecu(i:integer):integer;
Varj:integer;
Begin
Decu:=0;
For j:=a[i]to 3 do
Begin
Decu:=j;
Exit;
End;
End;
Procedurekhu_try;
Varj:integer;
Stop:boolean;

Begin
Stop: false;
While notstop do
Begin
Repeat
J:=decu(i);
If j>0then
Begin
X:=x+y[j];
I:=i+1;
If i>nthen begin inkq; j:=0; end;
If a[i]>3then a[i]:=1;
End;
Until j=0;
I:=i-1;
If i>0then begin a[i]: a[i] +1; delete(x,i,1);
End elsestop:=true;
End;
End;
Begin
Y:=’ABC’;
For i:=1 ton do a[i]:=1;
I:=1;
X:=’’;
Khu_try;
Readln;
End.
Từ bài toántrên ta có thể phát triển để giải quyết bài "Từ đẹp" (Bài 3a − Tìm chuỗi có độ
dài n xây dựng từ các ký tự A,B,Csao cho không có 2 chuỗi con liên tiếp giống nhau), với
các bổ xungsau:

- Viết thêm hàm KT: kiểmtra xem chuỗi có 2 chuỗi con liên tiếp hay không?
- Dùng mảng x: array[1..n]of char để lưu kết quả (để có độ dài lớn).
- Không duyệt tất cả cácdãy mà chỉ cần tìm một nghiệm đầu tiên (đặt exit trong lệnh
kiểmtra đủ cấu hình).
Do chỉ tìmmột nghiệm đầu tiên nên chương trình chạy tương đối nhanh, vớin=700 khoảng
1 giây, với n=10000 khoảng 2 phút (nếu dùng quay lui chỉ chạyđược với n≤29, nếu có dẫn
biên dịch {$M65520,0,655360} thì n≤123)
Sau đây là văn bản chươngtrình:
ProgramKhu_quay_lui_bai_tu_dep;
{$R-}
const n =10000;
typechuoi=array[1..10001] of char;
var
a:array[1..10001]of byte;
c,y: chuoi;
m:string[3];
i:integer;
functionkt(l:integer):boolean;
vart,k,m,d,c,c1:integer;
begin
kt:=true;
for t:=1 totrunc(1/2) do
begin
d:=1-2*t+1;
c:=1-t+1;
c1:=c;
m:=0;
Repeat
If y[d]=y[c]then m:=m+1;
D:=d+1;c:=c+1;

If m=t thenbegin kt:=false; exit; end;
Until d=c1;
End;
End;
Functiondecu(k:integer):integer;
Varz:integer;
Begin
Decu:=0;
For z=1 to ndo y[z]:=��;
For z:=1 tok-1 do y[z]:= c[z];
For z:=a[k]to 3 do
Begin
Y[k]:=m[z];
A[k]:=a[k]+1;
If kt(k)then
Begin
Decu:=z;
Exit;
End;
End;
End;
Procedureinkq(var q:chuoi);
Varj:integer;
Begin
For j:=1 ton do write(q[j]);
Writeln;
End;
Proceduretry;
Varj,k:integer;
Begin

Repeat
Repeat
J:= decu(j);
If j>0then {chap nhan j}
Begin
C[i]:=m[i];
If (i=n)then
Begin
Exit; {thoatkhong ghi nhan duoc nghiem dau tien}
{inkq(c);
if a[i]>3then begin a[i]:=1;j:=0; end;}
end else
i:=i+1;
if (i>n)then j:=0;
end
else ifa[i]>3 then begin a[i]:=1; end;
until (j=0);
i:=i-1;
if (i>0)then
begin
if(a[i]>3) then begin a[i]:=1;i:=i-1; end;
end;
until i=0;
end;
Begin
Fillchar(c,sizeof(c),’’);
For i:=1 ton do a[i]:=1;
M:=’ABC’;
I:=1;
Try;

Writeln(’Chuoican tim la:’);
Inkq(c);
Readln;
End.
*) Rõ ràng tađã biết, với 2 chữ cái thì bài toán "Từ đẹp " sẽ vô nghiệm khin ≥4. Vì với n=3
ta chỉ có 2 nghiệm là ABA và BAB, do chỉ có 2 khảnăng đề cử là A hoặc B nên không thể
thêm vào được nữa để sinhchuỗi có độ dài lớn hơn 3 (thêm vào sẽ vi phạm luật "đẹp
"ngay).
Với 3 chữ cái thì bài toán có nghiệm với mọi nnguyên dương. Vì có thể dựa vào 2 yều tố
sau:
Theokết quả từ chương trình, khi n càng lớn thì số từ đẹp càng tăngnhanh. Ví dụ: n=2 (có 6
nghiệm), n=3 (có 12 nghiệm), n=4 (có 18 nghiệm),n=5 (có 30 nghiệm), n=6 (có 42
nghiệm), n=7 (có 60 nghiệm), n=8 (có 78nghiệm), n=9 (có 108 nghiệm),&hưellip; Giảsử ta
đã xây dựng được từ đẹp có độ dài n≥2. - Nếu n chẵn thì bao giờcũng thêm được một ký tự
(khác với ký tự cuỗi hoặc áp cuỗi) vàosau để được từ đẹp có độ dài n+1.
- Những từ đẹp có độdài n+1 (lẻ) nếu không phải dạng "đối xứng gánh " cũng đều cóthể
thêm vào ký tự cuỗi để được từ đẹp mới.
Ví dụ từ đẹp "đối xứng gánh ": ACABACA khôngthể thêm vào 1 ký tự nào nữa, nhưng
không phải tất cả các nghiệmđều có dạng này.
*) Nếu thêm ràngbuộc "Bài 3b − cố định một chữ cái x, tìm từ đẹp có chiều dài n
saocho số lần xuất hiện của ký tự x là ít nhất " thì ta phải duyệt tất cả các từ đẹp và với
mỗi nghiệm ta đếmsố ký tự x (đếm khi xây dựng nghiệm), sau đó dùng thêm thủ
tụcTimKetQua để ghi nhận kỷ lục (số lần xuất hiện ký tự ít nhất).
*) Về nhậnđịnh "Nếu bài toán 3a giải được thì bài toán 3b cũng giải đượcvà ngược lại ",
theo tôi nhận xét như sau: Về mặt lý thuyết thì hiểnnhiên nhận định trên đúng nhưng thực
tế chỉ đúng với n nhỏ, cònn lớn thì bài toán 3b không thể giải quyết được (với n
khoảng10000?)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×