Thuật toán duyệt đệ quy
Trang 1Cùng chia sẻ những bài toán hay qua thuật toán duyệt đệ quy
Nguyễn Công Cường
Khi tiếp xúc với một bài toán, ít ai trong chúng ta nghĩ ngay đến phương pháp duyệt, mà ta luôn tìm đến những thuật toán mạnh hơn như: Quy hoạch động,cặp ghép… Bởi lẽ, vốn dĩ Duyệt thường không khả thi do tập hợp cáccấu hình cần duyệt là rất lớn Tuy nhiên, không phải lúc nào cácthuật toán mạnh cũng cho ra lời giải tốt nhất Mà đôi khi trong mộtsố trường hợp Duyệt lại là phương án tối ưu.
Nhưng phải duyệt như thế nào để vét cạn tất cả các trường hợp và hạn chếđược khối lượng các cấu hình cần duyệt lại là những câu hỏi tươngđối nhức đầu đặt ra cho chúng ta Sau đây, tôi xin giới thiêụ mộtsố bài toán hay sử dụng phương pháp đệ quy để duyệt
Bài toán 1 Xoay ô.
Cho một hình vuông gồm n*n ô, mỗi ô nhận được một trong 4 trạng thái sau:
Trạng thái của mỗi ô có thể thay đổi bằng cách xoay ô đó theo chiều kim đồnghồ một trong những góc 90o, 180o, 270o
Một hình vuông được gọi là hợp lệ nếu trong hình vuông bất kỳ 2 ô chung cạnhthì không chung màu
Ví dụ:
Yêu cầu: Tìm cách xoay ít ô nhất để hình vuông đã cho trở thành hợp lệ.
Dữ liệu vào: ″XOAY.INP″.
Dòng đầu gồm n dòng, n dòng tiếp theo mỗi dòng ghi trạng thái của hình chữ nhật
Kết quả:Ghi ra fiel″XOAY.OUT″.
Nếu không cần xoay thì ghi ′0′ Ngược lại ghi m −là số ô cần xoay m dòngtiếp theo mỗi dòng ghi toạ độ ô cần xoay và góc xoay
Ví dụ:
XOAY.INP
4
2 0 1 0
2 0 3 2
2 0 2 3
1 2 1 0
XOAY.OUT
6
Trang 21 1 270
2 1 270
2 3 180
2 4 180
3 2 270
4 2 180
Thuật toán:
Nhận thấyrằng: Nếu ô chéo nhau và liền dưới nhau đã biết thì 2 ô kề cạnh vớicả 2 ô đó là xác định duy nhất
Ví dụ:
Trong trườnghợp này: Để được hình vuông hợp lệ thì:
- Ô [1,2] bắtbuộc phải mang trạng thái 0
- Ô [2,1]nhất thiết phải mang trạng thái 1
Như vậy, mộthình vuông sẽ hoàn toàn xác định nếu ta đã biết trạng thái của cácô nằm trên đường chéo chính từ ô [1,1] đến ô [n,n]
Từ hình 1:Có duy nhất một hình vuông hợp lệ xây dựng được là:
Vậy, từ pháthiện trên, ta chỉ phải duyệt các ô nằm trên đường chéo chính củahình vuông, mỗi ô 4 trạng thái và cập nhật lại kỉ lục mỗi khi xâydựng được một hình vuông hợp lệ Chương trình;
ProgramXoay_o;
Constfi = ′Xoay.inp′;
fo=′Xoay.out′;
tren:array[0 3,0 3] of byte= ((0,1,1,0),(0,1,1,0),(3,2,2,3), (3,2,2,3));
Duoi:Array[0 3,0 3] of byte = ((0,0,3,3),(1,1,2,2),(1,1,2,2), 0,0,3,3));
Var a,c,l:array[1 10,1 10] of byte;
Sl,kl:Integer;
N: Byte; f:Text;
Procedureinput;
Var i,j:Integer;
Begin
Assign(f,fi);
Reset(f);
Trang 3For i:=1 to n do
For j:=1 to n do
Readln(f,a[i,j]);
Close(f);
End;
Procedurekhoi_tao;
Begin
Sl:=0; Kl:=maxint;
Fillchar(c,sizeof(c),0);
End;
ProcedureQlui(i: integer); Var j,k,dem:integer;
Begin
If i>n then
Begin
If sl
Begin
Kl:=sl; L:=c;
End;
End
Else
Begin
For j:=0 to 3 do
Begin
Dem:=0;
C[i,i]:=j;
If c[i,i]<>a[i,i] then inc(dem); For k:=i-1 downto 1 do
Begin
C[k,i]:=Tren[c[k,i-1],c[k+1,i]]; C[i,k]:=Duoi[c[i-1,k],c[i,k+1]]; Ifc[k,i]<> a[k,i] then inc(dem); Ifc[i,k]<>a[i,k] then inc(dem); End;
Sl:=sl+dem;Qlui(i+1);
Sl:=sl-dem;
End;
End;
End;
Procedureoutput
Var i,j,t: integer;
Begin
Assign(f,fo);
Rewrite(f);
Writeln(f,kl);
Trang 4If kl<>0 then
For i:=1 to n do
For j:=1 to n do
If l[i,j]<> a[i,j] then
Begin
Write(f,i,′′,j,′ ′);
T:=(l[i,j]-a[i,j]+4) mod 4;
Writeln(f,t*90);
End;
Close(f);
End;
Proceduremain;
Begin
Khoi_tao; Qlui; Output;
End;
BEGIN
Input;
Main;
END
Bài toán 2 Robot quét vôi
Có 9 cănphòng (đánh số từ 1 đến 9) đã được quét vôi với mầu trắng, xanhhoặc vàng Có 9 robot (được đánh số từ 1 đến 9) phụ trách việc quétvôi các phòng Mỗi robot chỉ quét vôi một số phòng nhất định Việcquét vôi được thực hiện nhờ một chương trình cài sẵn theo quy tắcsau:
- Nếu phòng đang có màu trắng thì quét màu xanh
- Nếu phòngđang có màu xanh thì quét màu vàng
- Nếu phòngđang có màu vàng thì quét màu trắng
Cần phải gọilần lượt một số các robot ra quét vôi (mỗi lần một robot, một robotcó thể gọi nhiều lần và cũng có thể không được gọi Robot đượcgọi quét vôi tất cả các phòng mà nó phụ trách) Hãy tìm một phươngán như vậy, sao cho lượng vôi phải quét là ít nhất Giả thiết rằnglượng vôi cho mỗi lượt quét với mỗi phòng là như nhau
Dữ liệu vào: ROBOT.INP
9 dòng đầutiên liệt kê danh sách các phòng (viết liền nhau) mà robot thứ i (i=1 9) phụ trách
Dòng cuốimô tả màu vôi ba đầu của các phòng với quy ước:
X- xanh; V− vàng; T - trắng
Kết quả:
Ghi ra file″ROBOT.OUT″ gồm 1 dòng dưới dạng:
- Nếu khôngcó phương án thì ghi ″0″
- Ngược lạighi dãy thứ tự các Robot được gọi
Ví dụ:
ROBOT.INP
159
123
257
Trang 55
369
456
789
258
XVXVXVTXT
Thuật toán: ở bài này, ta lại thấy rằng: thứ tự gọi robot ta quét vôi có tínhchất giao hoán,
nghĩa là robot thứ i dù được gọi trước hay sau đềukhông ảnh hưởng đến kết quả nhận được
Ví dụ: Nếudanh sách các robot được gọi là 121 thì ta cũng có thể gọi theo thứtự: 112 − kết
quả không hề thay đổi
Như vậy: mộtrobot nếu được gọi đến lần thứ 3 thì ta có thể bỏ robot đó ra khỏidanh sách, tức là nếu muốn nhận được kết quả tối ưu thì mỗi robotphải không được gọi quá 2 lần, từ
đó ta có thể đưa ra chương trìnhnhư sau:
Chương trình:
{$M65000,0,65000}
Programrobot_quet_voi;
Constfi=′Robot.inp′;
fo=′Robot.out′;
Typemang=array[1 9] of byte;
Var Rb:array[1 9] of mang;
a,d,luu:mang;
s,kq:string;
kl,sl:integer;
f:text;
ProcedureInput;
Vari,j: integer; Ch: char;
Begin
Assign(f,fi);
Reset(f);
For i:=1 to 9 do
Begin
J:=0;
While not eofn(f) do
Begin
Read(f,cj);
If ch<> #13 then
Begin
Inc(j);
Rb[i][j]:=ord(ch)-48;
End;
End;
d[i]:=j; read(f,s);
close(f);
End;
Trang 6Begin
Kq:=′TTTTTTTTT′; Fillchar(a,sizeof(a),0); Sl:=0; Kl:=maxint;
End;
ProcedureQlui(i,t: integer); Var j,k,l:integer;
Begin
If s=kq then
Begin
If sl
Begin
kl:=sl; luu:=a;
end;
end
else
begin
for j:=t to 9 do
if a[j] <2 then
Begin
Inc(a[j]) ;
For k:=1 to d[j] do
Begin
L:=Rb[j][k];
Case s[l] of
′T′: s[l]:=′X′;
X′: s[l]:=′V′;
′V′:=s[l]:=′T′;
end;
end;
sl:=sl+d[j];
Qlui(i+1,j);
Sl:=sl-d[j];
Dec(a[j]);
For k:=1 to d[j] do
Begin
L:=Rb[j][k];
Case s[l] of
′T′: s[l]:=′X′;
′X′: s[l]:=′V′;
′V′:=s[l]:=′T′;
end;
end;
end;
end;
Trang 7Procedureoutput;
Var i: integer;
Begin
Assign(f,fo);
Rewrite(f);
If kl=maxint thenWrite(f,0) Else
Begin
For i=1 to 9 do
begin
If luu[i]=1 then write(f,i);
If luu[i]=2 then write(f,i,i); End;
End;
Close(f);
End;
ProcedureMain;
Begin
Khoi_tao;
Qlui(1,1);
Output;
End;
BEGIN
Input;
Main;
END