Trong đó với các phép lặp, ta giải bài toán bằng cách thực hiện liên tiếp một số các câu lệnh trong vòng lặp cho tới khi một điều kiện nào đó được thỏa mãn.. Đệ quy quay lui là một trong
Trang 1PHẦN I: PHẦN MỞ ĐẦU
I Lý do chọn đề tài:
Ngôn ngữ lập trình là một trong những nội dung được đưa vào dạy chính thức
ở bộ môn Tin học ở nhà trường phổ thông Như ta đã biết, các câu lệnh để giải quyết bài toán Tin học được mô tả dưới các dạng tuần tự, rẽ nhánh và lặp Trong đó với các phép lặp, ta giải bài toán bằng cách thực hiện liên tiếp một số các câu lệnh trong vòng lặp cho tới khi một điều kiện nào đó được thỏa mãn
Một kỹ thuật lập trình được sử dụng để thay thế cho các phép lặp đó là kỹ thuật đệ quy quay lui (Back tracking) Mặt khác, trong thực tế có rất nhiều bài toán đòi hỏi sự lặp đi lặp lại một cách phức tạp Và đệ quy quay lui cung cấp cho ta cơ chế giải quyết bài toán phức tạp một cách đơn giản và dễ hiểu
Đệ quy quay lui là một trong những chiến lược để giải quyết nhiều bài toán, đặc biệt là các bài toán đòi hỏi liệt kê mọi cấu hình thoả mãn Đặc biệt trong đề thi học sinh giỏi Tỉnh các năm đệ quy quay lui luôn chiếm một phần quan trọng
Đã có nhiều bài viết đề cập đến đệ quy quay lui và các vần đề xung quanh đệ quy
quay lui nhưng trong tài liệu này, tôi muốn đề cập tới “Đệ quy quay lui trên
mảng hai chiều” để làm đề tài sáng kiến kinh nghiệm của mình
II Mục đích nghiên cứu:
Đề tài: “Đệ quy quay lui trên mảng hai chiều” nhằm đạt mục đích sau:
− Giúp cho bản thân có thể hiểu rõ hơn, sâu hơn về kỹ thuật đệ quy quay lui, ứng dụng đệ quy quay lui để giải các bài toán trong Tin học, từ đó nâng cao khả năng tự học, khả năng lập trình của bản thân
− Nâng cao chất lượng bồi dưỡng học sinh giỏi môn Tin các kỳ thi
− Góp phần làm đa dạng tài liệu nghiên cứu của tổ bộ môn
III Phương pháp nghiên cứu:
Khi nghiên cứu đề tài này tôi sử dụng các phương pháp sau:
− Phương pháp nghiên cứu lý thuyết
Trang 2− Sử dụng phương pháp thu thập thông tin và tổng hợp, phân tích, so sánh dựa trên lý thuyết về đệ quy quay lui.
− Áp dụng giải với một số bài toán cụ thể
IV Đối tượng nghiên cứu:
Đối tượng nghiên cứu của đề tài này đó chính là đệ quy quay lui và ứng dụng của quay lui để giải các bài toán trong Tin học có liên quan tới mảng hai chiều và các tài liệu, các thông tin liên quan
Nội dung nghiên cứu được trình bày theo cấu trúc sau:
1 Nhắc lại kiến thức về đệ quy quay lui (Back tracking)
2 Bài toán kinh điển: Mã đi tuần
3 Ứng dụng quay lui để giải một số bài toán xử lý trên mảng hai chiều
Kết luận
Trang 3PHẦN II: PHẦN NỘI DUNG
1 Nhắc lại kiến thức về đệ quy quay lui (Back tracking)
1.1 Tổng quan về đệ quy quay
lui
Về bản chất, tư tưởng của phương pháp là thử từng khả năng cho đến khi tìm thấy lời giải đúng Đó là một quá trình tìm kiếm theo độ sâu trong một tập hợp các lời giải Trong quá trình tìm kiếm, nếu ta gặp một hướng lựa chọn không thỏa mãn,
ta quay lui về điểm lựa chọn nơi có các hướng khác và thử hướng lựa chọn tiếp theo Khi đã thử hết các lựa chọn xuất phát từ điểm lựa chọn đó, ta quay lại điểm lựa chọn trước đó và thử hướng lựa chọn tiếp theo tại đó Quá trình tìm kiếm thất bại khi không còn điểm lựa chọn nào nữa
Quy trình đó thường được cài đặt bằng một hàm đệ quy mà trong đó mỗi thể hiện của hàm lấy thêm một biến và lần lượt gán tất cả các giá trị có thể cho biến đó, với mỗi lần gán trị lại gọi chuỗi đệ quy tiếp theo để thử các biến tiếp theo Chiến lược quay lui tương tự với tìm kiếm theo độ sâu nhưng sử dụng ít không gian bộ nhớ hơn, nó chỉ lưu giữ trạng thái của một lời giải hiện tại và cập nhật nó
Yếu tố mấu chốt trong một thủ tục đệ quy là điều kiện duyệt và điểm dừng Với đặc trưng của mảng hai chiều là phần tử mảng được xác định bằng hai giá trị hàng
và cột, thông thường chúng ta vẫn sử dụng cặp giá trị (i,j) (hàng, cột của 1 ô) làm bước duyệt khi gọi thủ tục đệ quy
1.2 Giải thuật tổng quát
Procedure Try(i); {Chọn thực hiện bước thứ i}
Begin
For CÁC PHƯƠNG ÁN CHỌN do
If CHỌN ĐƯỢC then
Begin
- THỰC HIỆN BƯỚC ĐI THỨ i;
- IF THÀNH CÔNG then THÔNG BÁO KẾT QUẢ
Else Try(i+1);
-HỦY BƯỚC ĐI THỨ i; {Quay lui}
End;
Trang 4- Thủ tục trên sẽ được khởi động bởi lệnh: Try(1);
2 Bài toán kinh điển: Mã đi tuần
2.1 Đề bài:
Mã đi tuần (hành trình của quân mã) là bài toán về việc di chuyển một
quân mã trên bàn cờ vua (8 x 8) Quân mã được đặt ở một ô trên một bàn cờ trống
nó phải di chuyển theo quy tắc của cờ vua để đi qua mỗi ô trên bàn cờ đúng một lần Hãy viết chương trình đặt quân mã vào một vị trí bất kỳ trên bàn cờ Tìm chu trình của quân mã để ghé thăm một lần duy nhất tất cả các ô còn lại
- Quy tắc di chuyển của quân mã trên bàn cờ vua: quân mã đi theo hình chữ L
2.2 Phân tích bài toán:
Có rất nhiều lời giải cho bài toán này, chính xác là 26.534.728.821.064 lời giải trong đó quân mã có thể kết thúc tại chính ô mà nó khởi đầu Một hành trình như vật được gọi là hành trình đóng
Có những hành trình, trong đó quân mã sau khi đi hết tất cả 64 ô của bàn cờ (kể cả ô xuất phát), thì từ ô cuối của hành trình không thể đi về ô xuất phát chỉ bằng một nước đi Những hành trình như vậy được gọi là hành trình mở
Đối với bài toán mã đi tuần ta nhận thấy rằng, ban đầu tọa độ của quân mã là (i,j) bất kỳ trên bàn cờ Tại vị trí này theo quy luật di chuyển của quân mã trên bàn
cờ thì quân mã có tối đa 8 hướng đi tiếp theo Rõ ràng để đi đến các lời giải ta phải thử tất cả các trường hợp quân mã có thể đi đến ở bước 1 Với mỗi vị trí thử như vậy ta lại thử tất cả các các trường hợp của quân mã có thể đi đến ở bước 2,…
Trang 5Cứ tiếp tục như vậy cho đến khi quân mã đi qua tất cả các ô trên bàn cờ vua thì dừng lại Như vậy, tính chất đệ quy của bài toán đó thể hiện trong các phép thử hướng đi của quân mã ở trên.
Kết quả ta tìm được nhiều chu trình đi khác nhau nhưng số bước đi đều là 63
Sử dụng giải thuật đệ quy quay lui cho bài toán mã đi tuần, ta có:
- Try(k,i,j): Chọn thực hiện bước đi thứ k, với ô xuất phát là (i,j);
- Các phương án chọn: Có 8 phương án (tương ứng với 8 trường hợp có
thể đi đến của quân mã):
Khi đó: phương án m là di chuyển mã đến ô (i+d[m], j+c[m])
- Chọn được: nếu như ô (i+d[m], j+c[m]) quân mã chưa đi qua và 1<=
i+d[m]<=8 và 1<=j+c[m]<= 8
- Thực hiện bước đi thứ k:
+ Tổ chức lưu trữ: Dùng mảng 2 chiều A có kích thước 8x8: A[1 8,
1 8] A[i,j] = 0 : nếu quân mã chưa đi qua ô (i,j)
k : nếu quân mã đã đi qua ô (i,j) ở bước thứ k
+ Lưu vết: A([i+d[m], j+c[m]):=k.
- Thành công: k=64 (hoặc k>63).
- Thông báo kết quả: In mảng A.
- Lời gọi đệ quy tiếp theo: Try(k+1, i+d[m], j+c[m]).
Trang 6- Hủy bước đi thứ k: A([i+d[m], j+c[m]):=0.
* Thủ tục đệ quy quay lui cho nài toán này được viết như sau:
Trang 7var x, y:integer; Begin
Trang 8* Chạy thử chương trình: Dưới đây là một đường đi của quân mã:
3 Ứng dụng quay lui để giải một số bài toán xử lý trên mảng hai chiều
3.1 Ứng dụng quay lui để giải một số bài toán xử lý trên mảng hai chiều kích thước NxN.
3.1.1 Kiểm tra có đường đi giữa 2 đỉnh của đồ thị
a, Đề bài: Sơ đồ đường đi giữa N điểm (có số hiệu 1, 2, …, N) trong thành phố X
được cho bởi ma trận A kích thước NxN Mỗi phần tử của ma trận A chỉ có thể là 0 hoặc 1 Nếu phần tử ở hàng i, cột j của A bằng 1 thì có đường đi từ địa điểm i tới địa điểm j, ngược lại nếu không có đường đi (1≤i,j≤N)
Hãy kiểm tra giữa hai địa điểm P, Q có đường đi hay không
Dữ liệu vào là tệp văn bản LIENTHONG.INP có cấu trúc:
- Dòng đầu tiên ghi số N (0<N≤100)
- N dòng tiếp theo, mỗi dòng gồm N số là các giá trị của ma trận A
- Dòng cuối cùng ghi 2 địa điểm P, Q Các số cách nhau ít nhất một ký tự trống
Dữ liệu ra là tệp LIENTHONG.OUT ghi dòng thông báo “Co duong di:” cùng với đường đi đầu tiên tìm được đi nối từ P sang Q hoặc dòng thông báo “Khong co
duong di”
Trang 9Ví dụ:
Tệp LIENTHONG.INP Tệp LIENTHONG.OUT 4
Để tìm đường đi từ P sang Q thì ta bắt đầu từ việc tìm đường đi từ P tới các điểm j
có đường nối trực tiếp với P, tức là A[p,j]=1 và chưa được xét (cx[j]=true) thì ta đánh dấu là j đã được kết nạp Nếu điểm j vừa kết nạp chính là Q thì thông báo có đường đi và kết thúc việc tìm kiếm, ngược lại thì thử với điểm j vừa được kết nạp Lặp lại quá trình trên cho tới khi tất cả các đỉnh đã được xét tới Vì bài toán này chỉ cần kiểm tra có đường đi từ điểm P sang Q nên ta không cần trả lại trạng thái cho đỉnh vừa được kết nạp
Nếu sau khi quá trình lặp kết thúc, ta xét cx[Q] = True thì chứng tỏ không có đường đi từ P sang Q
c, Chương trình:
PROGRAM KIEM_TRA_LIEN_THONG_GIUA_2_DINH_P_Q_QUAY_LUI;
const fi='LIENTHONG.INP'; fo='LIENTHONG.OUT';
var x:array[1 100] of integer;
a:array[1 100,1 100] of integer;
N,K,I,j,p,q,dem:INTEGER;
Cx:array[1 100] of BOOLEAN;
f:text;
Trang 10{ -Thu tuc doc du lieu -}
write(F,'Co duong di:');
for i:=1 to dem-1 do write(f,x[i], '->');
for j:=1 to n do {xét các địa điểm của thành phố}
if (a[d,j]=1) and (cx[j]) then {nếu có đường đi từ d sang j và điểm
j chưa xét}
begin
cx[j]:=false; dem:=dem+1;
x[dem]:=j;
{nếu điểm j vừa xét chính là điếm cuối Q thì in kết quả và kết thúc}
if j=Q then begin inkq ; exit; end
else try(j,c); {ngược lại thì xét giữa điểm j và điểm cuối có
đường nối không}
Trang 113.1.2 Kiểm tra tính liên thông giữa nhiều cặp đỉnh của đồ thị
a, Đề bài: Sơ đồ đường đi giữa N điểm (có số hiệu 1, 2, …, N) trong thành phố X
được cho bởi ma trận A kích thước NxN Mỗi phần tử của ma trận A chỉ có thể là 0 hoặc 1 Nếu phần tử ở hàng i, cột j của A bằng 1 thì có đường đi từ địa điểm i tới địa điểm j, ngược lại nếu không có đường đi (1≤i,j≤N)
Hãy kiểm tra giữa hai địa điểm P, Q có đường đi hay không
Dữ liệu vào là tệp văn bản LTN.INP có cấu trúc:
- Dòng đầu tiên ghi số N (0<N≤100)
- N dòng tiếp theo, mỗi dòng gồm N số là các giá trị của ma trận A
- Các dòng tiếp theo mỗi dòng ghi 2 địa điểm P, Q bất kỳ
Trang 12b, Phân tích
Đối với bài này không chỉ có một cặp địa điểm nên việc đọc và ghi dữ liệu phải thực hiện đồng thời
Thủ tục Try (d,c) hoàn toàn tương tự bài 3.1.
Trong thủ tục Doc_xuly cần lưu ý điểm sau:
- Mỗi lần đọc cặp địa điểm thì thực hiện thủ tục Try(d,c), sau khi xét có đường đi giữa 2 địa điểm vừa xét thì cần trả về trạng thái khởi tạo cho mảng đánh dấu cx để
xét tiếp cho các cặp địa điểm tiếp theo
Trang 13FOR I:=1 TO N DO Cx[I]:=TRUE;
WHILE NOT EOF(F) DO
a, Đề bài: Tương tự bài 3.1 nhưng yêu cầu là liệt kê tất cả các đường đi khác
nhau giữa hai đỉnh P và Q trong thành phố X Số hiệu mỗi địa điểm xuất hiện không quá một lần
Trang 14Ví dụ:
Tệp DUONG.INP Tệp DUONG.OUT 4
b, Phân tích: Tương tự bài 3.1 nhưng có 3 điểm cần bổ sung:
- Để tìm hết tất cả các đường đi thì cần trả lại trạng thái cho điểm vừa kết nạp để tiến tới việc xét các điểm khác nhằm tìm ra con đường đi mới
- Cần có một biến ktra lưu kết quả có đường đi hay không phục vụ cho việc in ấn kết quả (Vì mỗi lần ta lại trả lại trạng thái cho điểm mới kết nạp nên cx[Q] cuối cũng vẫn là True)
- Trong thủ tục in kết quả cần sử dụng thủ tục append để ghi dữ liệu.
c, Chương trình:
PROGRAM LIET_KE_DUONG_DI_GIUA_2_DINH_P_Q;
const fi='DUONG.INP'; fo='DUONG.OUT';
var x:array[1 100] of integer;
Trang 15write(F,'Co duong di:');
for i:=1 to dem-1 do write(f,x[i], '->');
Trang 16write(f,'Khong co duong di');close(f);
end;
END.
3.2 Ứng dụng quay lui để giải một số bài toán xử lý trên mảng hai chiều kích thước MxN.
3.2.1 Đếm số miền liên thông, miền liên thông lớn nhất.
a, Đề bài: Cho ma trận A cấp MxN, trên mỗi ô A[i,j] của lưới ghi số 0 hoặc 1
Hai ô được gọi là liên thông trực tiếp nếu nó chung cạnh và cùng giá trị 1, ngược lại
có giá trị 0 Hãy cho biết lưới ô vuông có bao nhiêu miền liên thông và miền liên thông nào là lớn nhất (có nhiều ô có giá trị 1 nhất) cùng với chỉ số của các ô đó
Dữ liệu vào: Tệp văn bản DTLT.INP có dạng:
- Dòng đầu tiên gồm 2 số M, N
- M dòng tiếp theo mỗi dòng ghi N số 0 hoặc 1
Dữ liệu ra: Tệp văn bản DTLN.OUT có dạng:
- Dòng đầu tiên ghi số liên thông
- Dòng tiếp theo ghi dòng thông báo diện tích vùng liên thông lớn nhất
- Các dòng tiếp theo ghi các vị trí của các ô thuộc vùng liên thông lớn nhất.Mỗi số phân cách nhau ít nhất một ký tự trống
Trang 17dx:array[1 4] of integer=(-1,1,0,0);
dy:array[1 4] of integer=(0,0,1,-1); {Trên,dưới,phải,trái}
Ta sẽ xét tất cả các ô có trong lưới MxN nếu có giá trị là 1 và chưa được xét thì ghi nhận có một miền liên thông Để xác định ô đã được xét hay chưa ta dùng một mảng hai chiều b Ban đầu cho b toàn số 0 Sau khi thực hiện việc kiểm tra tại ô a[i,j] nào đó, nếu nó thuộc miền liên thông nào thì sẽ xác định lại giá trị ô b[i,j] bằng chính chỉ số miền liên thông vừa tìm được, đồng thời tăng diện tích miền liên thông
(biến dem) lên 1 đơn vị
Với mỗi miền liên thông ta so sánh với giá trị max, nếu số ô trong miền liên thông
vừa tìm lớn hơn max thì ta gán giá trị của biến dem cho max, chỉ số miền liên thông lớn nhất csmax chính bằng solt Từ ô này ta xét các ô khác xung quanh chưa được
xét và có giá trị bằng 1 để kết nạp ô đó vào miền liên thông vừa xác định được Lặp lại việc này cho tới khi hết các ô trong luới
dy:array[1 4] of integer=(0,0,1,-1); {Len,xuong,phai,trai}
VAR a,b:array[0 maxM+1,0 maxM+1] of byte;
Trang 18for i:=1 to 4 do {xet 4 vi tri xung quanh diem (r,c)}
if (a[r+dx[i],c+dy[i]]=1) and (b[r+dx[i],c+dy[i]]=0) then
Trang 19writeln(F,'So lien thong:',solt);
writeln(F,'Vung lien thong co dien tich lon nhat=',max);
writeln(F,'Tai cac vi tri:');
Trang 20phát sóng nào đó nằm trên hai mảnh đất ô vuông có cùng một vùng phát sóng tại một thời điểm phát sóng nào đó hay không có ý nghĩa cho việc lên kế hoạch phát sóng trong lần phát sóng kế tiếp.
Cho trước vị trí của hai mảnh đất ô vuông, hãy xác định hai mảnh đất đó có cùng một vùng phát sóng tại một thời điểm phát sóng nào đó hay không?
Dữ liệu vào là tệp văn bản TSPS.INP có cấu trúc:
− Dòng đầu tiên ghi 2 số M, N (1≤M≤100, 1≤N≤100)
− M dòng tiếp theo, mỗi dòng ghi N ký tự ‘A’ hoặc ‘B’ nếu máy phát sóng đặt trên mảnh đất ô vuông tương ứng phát sóng loại sóng có dải tần số A hoặc B trong một lần phát sóng
− Các dòng tiếp theo, mỗi dòng ghi 4 số mô tả vị trí của hai mảnh đất ô vuông
mà ta cần kiểm tra chúng có cùng một vùng phát sóng hoặc không 2 số đầu ghi thứ tự dòng và số thứ tự cột của mảnh đất ô vuông thứ nhất, 2 số sau là thứ tự dòng và số thứ tự cột của mảnh đất ô vuông thứ hai
− Các ký tự trên cùng một dòng được ghi liên tiếp nhau Các số trên cùng một dòng được ghi cách nhau ít nhất một ký tự trống
Dữ liệu ra là tệp TSPS.OUT có số dòng là số cặp mảnh đất ô vuông mà ta cần kiểm tra xem chúng có cùng một vùng phát sóng hay không? Mỗi dòng ghi một số
là số 1 hoặc là số 0 nếu cặp mảnh đất ô vuông tương ứng có cùng một vùng phát sóng hay không
Ví dụ:
Tệp TSPS.INP Tệp TSPS.OUT
4 6 AABBAA BAABAA BBAABB AABBBB
1 2 3 4
2 3 1 6
1 0
b, Phân tích
Trang 21Bài toán này giải quyết hoàn toàn tương tự như bài 3.1.2 – Kiểm tra tính liên thông giữa nhiều cặp đỉnh của đồ thị Ở đây chỉ khác là ma trận kích thước MxN nên thủ tục Try phải bổ sung các tham số là chỉ số dòng và cột của hai điểm.
dy:array[1 4] of integer=(0,0,1,-1); {Len,xuong,phai,trai}
VAR a:array[0 maxM+1,0 maxM+1] of CHAR;
for i:=1 to 4 do {xet 4 vi tri xung quanh diem (X1,Y1)}
if (a[X1+dx[i],Y1+dy[i]]=a[X1,Y1]) and (CX[X1+dx[i],Y1+dy[i]])