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

SKKN sử DỤNG THUẬT TOÁN QUAY LUI để GIẢI các DẠNG bài TOÁN LIỆT kê BẰNG PHƯƠNG PHÁP vét cạn TRONG ôn LUYỆN học SINH GIỎI môn TIN học

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 (187.81 KB, 23 trang )

SỞ GIÁO DỤC VÀ ĐÀO TẠO THANH HÓA
TRƯỜNG THPT QUẢNG XƯƠNG 1

SÁNG KIẾN KINH NGHIỆM

SỬ DỤNG THUẬT TOÁN QUAY LUI ĐỂ GIẢI CÁC
DẠNG BÀI TOÁN LIỆT KÊ BẰNG PHƯƠNG PHÁP VÉT
CẠN TRONG ÔN LUYỆN HỌC SINH GIỎI MÔN TIN
HỌC

Người thực hiện: Phạm Văn Cường
Chức vụ: Tổ trưởng
SKKN thuộc bộ môn: Tin học

THANH HÓA NĂM 2021


MỤC LỤC
A. ĐẶT VẤN ĐỀ............................................................................................3
I. Lý do chọn đề tài..........................................................................................3
II. Thực trạng của vấn đề.................................................................................3
III. Mục đích nghiên cứu.................................................................................4
IV. Đối tượng và phạm vi nghiên cứu..............................................................4
B. GIẢI QUYẾT VẤN ĐỀ............................................................................5
I. Cơ sở lý luận.................................................................................................5
II. Biện pháp giải quyết vấn đề..........................................................................
1. Một số khái niệm.........................................................................................5
2. Một số dạng bài toán cấu hình tổ hợp..........................................................7
3. Một số bài tốn tham khảo.........................................................................19
III. Kết quả thực nghiệm................................................................................19
C. KẾT LUẬN VÀ ĐỀ XUẤT....................................................................20


I. Kết luận.......................................................................................................20
II. Đề xuất, kiến nghị.....................................................................................20

2


A. ĐẶT VẤN ĐỀ
I. LÝ DO CHỌN ĐỀ TÀI
Trong thời đại ngày nay, thế giới đang diễn ra quá trình tin học hóa trong
nhiều lĩnh vực hoạt động của xã hội. Tin học phát triển nhanh như vũ bão và đã trở
thành một ngành khoa học đóng vai trị quan trọng không thể thiếu đối với sự phát
triển của xã hội.
Nhiều quốc gia ý thức được tầm quan trọng của tin học và có những đầu tư lớn
vào lĩnh vực này đặc biệt là lĩnh vực giáo dục nhằm đào tạo một đội ngũ tri thức trẻ
có nền tảng tin học vững vàng nhằm đáp ứng nhu cầu ngày càng cao của xã hội.
Từ năm 2006 ngành giáo dục Việt Nam đã chính thức đưa bộ mơn tin học vào
trong trường THPT nhằm mục đích phổ cập các kiến thức cơ bản về Tin học, ngồi
ra cịn giúp học sinh có khả năng phân tích, tổng hợp, trừu tượng hóa, khái quát hóa
vấn đề, đặc biệt là phát triển khả năng tư duy. Muốn vậy ngoài việc dạy đại trà,
hướng nghiệp và dạy nghề cần tạo điều kiện cho học sinh có năng khiếu tin học
được phát triển khả năng lập trình để giải quyết tốt các bài tốn.
Để có thể phát huy những tài năng tin học thông qua ôn luyện đội tuyển học
sinh giỏi, đòi hỏi người dạy phải tiếp cận với nhiều dạng bài tốn khó và nắm vững
các phương pháp giải quyết bài tốn đó.
Bài tốn trong tin học thường rất đa dạng và phức tạp, mỗi bài tốn có thể có
nhiều phương pháp giải khác nhau. Để có thể lựa chọn phương pháp thích hợp cho
bài tốn, chúng ta có thể phân chia các bài toán thành các dạng bài toán tổng quan
và chỉ ra phương pháp giải cho các dạng bài tốn đó.
Bài tốn liệt kê là một trong những lớp bài tốn khó, thường xuất hiện trong
các đề thi học sinh giỏi cấp thành phố, cấp tỉnh hay cấp quốc gia. Có nhiều phương

pháp giải lớp bài toán này nhưng phương pháp vét cạn là phù hợp nhất. Chính vì
vậy nên tơi chọn đề tài: “Sử dụng thuật toán quay lui để giải các dạng các bài
toán liệt kê bằng phương pháp vét cạn trong ôn luyện học sinh giỏi môn Tin
học”
II. THỰC TRẠNG CỦA VẤN ĐỀ
1. Thuận lợi
Do sự quan tâm và đầu tư của Bộ giáo dục và đào tạo nói chung và của trường
THPT Quảng Xương 1 nói riêng, về cơ sở vật chất mơn Tin học đã có 1 phịng thực
hành hoạt động tốt, ngồi ra cịn có một số phòng máy chiếu projector hỗ trợ cho
giáo viên trong công tác giảng dạy.
Mặc dù môn Tin không phải là môn trọng điểm nhưng rất được Ban giám hiêu
quan tâm động viên và tạo mọi điều kiện trong công tác giảng dạy và ôn luyện đội
tuyển học sinh giỏi cũng như trong các công tác khác.

3


Trong q trình thực hiện đề tài tơi đã được các giáo viên trong tổ bộ môn tư
vấn và hỗ trợ rất nhiều giúp tơi hồn thành đề tài.
2. Khó khăn
Ngơn ngữ lập trình Pascal là một mơn học mới, cách học cũng hồn tồn mới,
vì vậy khi tiếp cận với môn học này đa số học sinh thấy rất bỡ ngỡ. Học các thao
tác sử dụng hay dùng phương pháp học thuộc lịng khơng cịn phù hợp nữa. Lúc
này các em cần phải học cách tư duy logic, tìm thuật tốn, và viết những dịng lệnh
máy tính chính xác đến từng đấu chấm, dấu phẩy.
Với tâm lí thơng thường các em học sinh coi tin học là môn phụ không quan
trọng nên nhiều em chủ quan không dành đủ thời gian để học nên không hiểu bài
và dần bị mất căn bản. Đây cũng là lí do mà nhiều em bị điểm kém, thậm chí là thi
lại, học lại bộ mơn tin học mặc dù có thể các em học rất giỏi các mơn học khác.
Chính vì xem mơn Tin là môn phụ nên khi lựa chọn đội tuyển ôn thi học sinh

giỏi giáo viên chúng tôi thường rất khó lựa chọn được những học sinh có năng
khiếu thực sự.
Các dạng bài toán liệt kê là những bài toán khó, nếu người học có tư duy chưa
tốt thì người dạy sẽ rất khó khăn trong q trình truyền đạt để giúp các em có thể
hiểu được.
3. Kết quả của thực trạng
Trước khi đi vào nghiên cứu và thực hiện đề tại này, tôi thường hướng dẫn học
sinh tiếp cận với các bài toán liệt kê bằng cách cho bài tập và cách giải với từng bài
mà không phân chia thành từng dạng cụ thể và thuật toán tổng quan cho từng dạng,
dẫn đến học sinh rất mơ hồ, khó hiểu. Kết quả khảo sát trong quá trình giảng dạy và
thông qua các bài kiểm tra như sau:
N ội d u n g cầ n nắ m bắt
Số lượng
Hiểu được kiến thức vừa học
3/6
Tự viết lại chương trình đã được giới thiệu
3/6
Vận dụng kiến thức đã học để làm tốt các
1/6
bài tập

Tỉ lệ (%)
50%
50%
16,7%

4


B. GIẢI QUYẾT VẤN ĐỀ

I. CƠ SỞ LÝ LUẬN
Bài toán liệt kê (hay cịn gọi là bài tốn tổ hợp) là những bài toán khi cho
trước một tập các đối tượng, yêu cầu cho biết có bao nhiêu đối tượng thõa mãn
những điều kiện nhất định và chỉ rõ những cấu hình tìm được thõa mãn những điều
kiện trên.
Để giải được bài toán liệt kê cần xác định được thuật tốn tổng quan để trên cơ
sở đó xây dựng được tất cả các cầu hình liên quan.
Khi đi vào giải quyết bài tốn liệt kê, u cầu các cấu hình tìm được khơng
được lặp lại và khơng được bỏ sót cầu hình nào. Để đáp ứng được yêu cầu trên thì
phương pháp vét cạn sự lựa chọn thích hợp nhất.
II. BIỆN PHÁP GIẢI QUYẾT VẤN ĐỀ
1. Một số khái niệm
1.1. Vét cạn
Phương pháp vét cạn: là phương pháp tìm nghiệm của bài toán bằng cách xem
xét tất cả các phương án có thể.
Đối với con người, phương pháp này khơng khả thi vì số phương án cần kiểm
tra quá lớn, nhưng nhờ có máy tính mà rất nhiều bài tốn được giải quyết bằng
phương pháp này
*Ưu điểm:
- Luôn đảm bảo tìm ra nghiệm chính xác
- Địi hỏi rất ít bộ nhớ và cài đặt đơn giản
* Nhược điểm: Thời gian thực thi rất lớn, độ phức tạp thường bậc mũ
1.2. Quay lui (Backtracking)
Trong nhiều trường hợp, nghiệm của bài toán là dãy các phần tử được xác
định không theo luật tính tốn nhất định. Muốn tìm nghiệm, phải thực hiện từng
bước theo dõi, tìm kiếm từng phần tử của nghiệm. Giá trị mỗi phần tử có thể có
nhiều khả năng khác nhau. Để tìm giá trị gán cho mỗi phần tử, phải thử các khả
năng có thể xem chúng có thõa mãn các điều kiện của bài tốn hay khơng gọi là thử
đúng/sai
Nếu có một lựa chọn được chấp nhận thì ghi nhớ các thơng tin cần thiết các

bước thử tiếp theo.Trái lại, nếu khơng có một lựa chọn nào thích hợp thì làm lại
bước trước, xố bớt các ghi nhớ và quay về chu trình thử với các lựa chọn còn lại.
Hành động này được gọi là quay lui (Back tracking) và các giải thuật thể hiện
phương pháp này gọi là các giải thuật quay lui.
Tìm mọi nghiệm (gọi là vét cạn) bằng cách tiến dần, tìm kiếm các khả năng có
thể chấp nhận được cho từng phần tử của một nghiệm và biết quay lui khi không
thể tiến được nữa. Khi mọi phần tử của một nghiệm đã được gán giá trị thì kết thúc
quá trình tìm một nghiệm, chuyển sang tìm nghiệm tiếp theo.
5


Do thuật toán quay lui xây dựng trên cơ sở tìm kiếm dần, kết quả sau hình
thành từ kết quả trước nên có thể sử dụng các hàm và các thủ tục đệ quy để thực
hiện.
Có thể so sánh các nghiệm tìm được để tìm nghiệm tối ưu.
1.3. Thuật tốn quay lui
Mơ hình giải thuật quay lui có thể mơ tả như sau:
Procedure Try (i);
Begin
Vòng lặp đề của mọi khả năng của bước i
Begin
- Thử chọn một đề cử cho bước i
- Nếu đề cử này thõa mãn điều kiện thì
Begin
+ Lưu trạng thái của bài tốn
+ Xác nhận giá trị đề cử cho bước i
+ Xác nhận trạng thái mới của bài toán sau khi chấp nhận đề cử
+ Nếu là bước cuối cùng thì hiện nghiệm và tăng biến đếm nghiệm
Ngược lại thì Try(i+1)
+Trả lại trạng thái như trước khi chấp nhận đề cử

End;
End;
End;
Hoặc có thể viết dưới dạng sau:
Procedure Try (i);
Begin
+Nếu là bước cuối cùng thì hiện nghiệm và tăng biến đếm nghiệm
Ngược lại thì
vịng lặp đề của mọi khả năng của bước i
Begin
- Thử chọn một đề cử cho bước i
- Nếu đề cử này thõa mãn điều kiện thì
Begin
+Lưu trạng thái của bài tốn
+Xác nhận giá trị đề cử cho bước i
+Xác nhận trạng thái mới của bài toán sau khi chấp nhận đề cử
+Try(i+1)
+Trả lại trạng thái như trước khi chấp nhận đề cử
End;
End;
End;
6


Giả mã cụ thể của thuật toán như sau:
Procedure Update;
Begin
count := count + 1;
print(x);
end;

Procedure Try(i);
Var j;
Begin
For j:=1 to m do
If <chọn được a[j]> then
Begin
X[i]:=a[j];
<ghi nhận trạng thái mới>;
If i=n then Update
Else Try(i+1);
<Trả lại trạng thái cũ>;
End;
End;
Procedure Search;
Begin
Try(1);
End;
Trên đây là các thuật tốn vét cạn đối với bài tốn tìm mọi cấu hình hay đếm
số cấu hình. Trong trường hợp bài tốn cần tìm một cấu hình, tìm cấu hình tối ưu
thì thuật tốn cũng tương tự, chỉ khác ở phần cập nhật (Update) khi sinh được một
cấu hình mới.
Chẳng hạn thủ tục Update đối với bài tốn tìm nghiệm tối ưu:
procedure Update;
begin
If <f(x) tốt hơn f(best)> then best:=x;
end;
Ở đây chúng ta không xét việc áp dụng phương pháp vét cạn sử dụng đệ quy
quay lui dùng cho các bài toán đơn thuần mà tập trung vào các bài tốn tìm cấu
hình tổ hợp, tối ưu tổ hợp
2. Một số dạng bài tốn cấu hình tổ hợp

Bài tốn tổ hợp u cầu tìm các đối tượng x có dạng là một vector thõa mãn
các điều kiện sau:
1. x gồm n phần tử: x=(x1,x2,…,xn)
7


2. Mỗi phần tử xi có thể nhận một trong tập các đối tượng a1,a2,…,am,
3. x thõa mãn các ràng buộc có thể cho bởi hàm logic G(x)
Tùy từng trường hợp bài tốn có thể u cầu tìm một nghiệm, tìm tất cả các
nghiệm hay đếm số nghiệm
2.1. Tổ hợp
Một tổ hợp chập k của n là một tập con k phần tử của tập n phần tử.
Chẳng hạn tập {1,2,3,4} có các tổ hợp chập 2 là: {1,2}, {1,3, {1,4, {2,3},
{2,4}, {3,4}. Vì trong tập hợp các phần tử không phân biệt thứ tự nên tập {1,2}
cũng là tập {2,1} và do đó, ta coi chúng chỉ là một tổ hợp.
Bài toán đặt ra cho chúng ta là hãy xác định tất cả các tổ hợp châp k của tập n
phần tử. Để đơn giản ta chỉ xét bài tốn tìm các tổ hợp của tập các số ngun từ 1
đến n. Đối với một tập hữu hạn bất kì, bằng cách đánh số thứ tự của các phần tử, ta
cũng đưa được về bài toán đối với tập các số nguyên từ 1 đến n.
Nghiệm cần tìm của bài tốn tìm các tổ hợp chập k của n phần tử phải thoả
mãn các điều kiện sau:
1. Là một vector x =(x1,x2,…xk)
2. xi lấy giá trị trong tập {1,2,…n}
3. Ràng buộc: xiCó ràng buộc 3 là vì tập hợp khơng phân biệt thứ tự phần tử nên ta sắp xếp các
phần tử theo thứ tự tăng dần.
Ví dụ: Tìm các tổ hợp chập k của n
*Phân tích:
Để liệt kê các tập con k phần tử của tập S={1, 2, …, n} ta đưa về liệt kê các
cấu hình x[1..n] với x[i] thuộc tập S và x[1] < x[2] < …< x[n]

Nhận xét:
X[k] ≤ n
X[k-1] ≤ x[k] - 1 ≤ n-1

X[i] ≤ n - k + i

X[1]≤ n - k +1
Từ đó suy ra x[i-1] + 1≤ x[i] ≤ n-k+i (1≤ i ≤ k). Giả thiết có thêm số x[0] =
0 khi xét i=1 (x[0] là lính canh)
Về cấu trúc dữ liệu ta chỉ cần một mảng x để biểu diễn tổ hợp. Ràng buộc đối
với giá trị x[i] là: x[i−1]< x[i] ≤ n−k+i.
*Chương trình
Program Tohop;
uses crt;
const
8


max = 20;
input='TOHOP.INP';
output='TOHOP.OUT' ;
var n,k : integer;
x : array[0..max] of integer;
f1,f2:text;
procedure readfile;
var f:text;
begin
clrscr;
assign(f1,INPUT);reset(f1);
assign(f2,OUTPUT);rewrite(f2);

readln(f1,n,k);
end;
procedure print;
var i : integer;
f: text;
begin
for i := 1 to k do write(f2,x[i]);
writeln(f2);
end;
procedure try(i:integer);
var j : integer;
begin
for j := x[i-1]+1 to n-k+i do
begin
x[i] := j;
if i = k then Print
else try(i+1);
end;
end;
BEGIN
Readfile;
x[0] := 0;
try(1);
close(f1); close(f2);
END.

9


2.2. Chỉnh hợp lặp

Chỉnh hợp lặp chập k của n là một dãy k thành phần, mỗi thành phần là một
phần tử của tập n phần tử, có xét đến thứ tự và không yêu cầu các thành phần khác
nhau.
Như vậy, bài toán xác định tất cả các chỉnh hợp lặp chập k của tập n phần tử
yêu cầu tìm các nghiệm như sau:
1. Là một vector x =(x1,x2,…xk)
2. xi lấy giá trị trong tập {1,2,…n}
3. Khơng có ràng buộc nào giữa các thành phần.
Chú ý là cũng như bài tốn tìm tổ hợp, ta chỉ xét đối với tập n số nguyên từ 1
đến n. Nếu tập hợp cần tìm chỉnh hợp khơng phải là tập các số ngun từ 1 đến n
thì ta có thể đánh số các phần tử của tập đó để đưa về tập các số nguyên từ 1 đến n
Đối với bài toán sinh chỉnh hợp lặp chập k của n hồn tồn khơng có ràng
buộc nào đối với cấu hình sinh ra. Do đó, cấu trúc dữ liệu của ta chỉ gồm một mảng
x để lưu nghiệm.
Thuật toán sinh chỉnh hợp lặp như sau:
procedure Try(i);
var j;
begin
for j := 1 to n do
begin
x[i] := j;
if i=k then Print(x)
else Try(i+1);
end;
end;
Ví dụ 1: Liệt kê dãy nhị phân độ dài n (n nguyên dương)
*Phân tích
Một ví dụ dễ thấy nhất của chỉnh hợp lặp là các dãy nhị phân. Một dãy nhị
phân độ dài m là một chỉnh hợp lặp chập m của tập 2 phần tử {0,1}. Ví dụ dãy nhị
phân độ dài n=3 gồm 8 số: 101, 000, 001, 010, 011, 100, 110, 111. Vì có xét thứ tự

nên dãy 101 và dãy 011 là 2 dãy khác nhau.
*Chương trình
Program Nhiphan;
uses crt;
const max = 20;
input=’nhiphan.inp’;
output=’nhiphan.out’;
var
n : integer;
10


x : array[1..max] of integer;
f1,f2: text;
procedure readfile;
begin
clrscr;
assign(f1, input); reset(f1);
assign(f2,output); rewrite(f2);
readln(f1,n);
writeln(f2, 'Cac day nhi phan do dai ',n);
end;
procedure print;
var
i : integer;
begin
for i := 1 to n do write(f2,x[i]);
writeln(f2);
end;
procedure try(i:integer);

var
j : integer;
begin
for j := 0 to 1 do
begin
x[i] := j;
if i = n then Print
else try(i+1);
end;
end;
BEGIN
Readfile;
try(1);
close(f1); close(f2);
END.
Cây tìm kiếm quay lui liệt kê dãy nhị phân độ dài n = 3:

11


Ví dụ 2: Biểu thức Zero:
Cho số tự nhiên N (N≤ 9). Giữa các số 1, 2,…, N hãy chèn thêm vào các dấu
cộng (+) và trừ (-) sao cho được biểu thức có giá trị bằng 0. Hãy viết chương trình
tìm tất cả các khả năng có thể.
*Phân tích
Với bài toán này chúng ta sẽ dùng một mảng d[j] (j=1..2)chứa dấu cộng và trừ,
một mảng x[i] (i=2..n) chứa các phép toán tương ứng được chèn vào giữa các số 1,
2,…, N. Vậy nghiệm bài tốn được mơ tả như sau:
1. X là một vector, x=(x2,x3,…,xn)
2. xi nhận các giá trị trong tập {d1,d2}

3. Khơng có ràng buộc giữa các giá trị
*Chương trình
Program Bieuthuc_Zero;
const
d:array[1..2] of char=('+','-');
input='zero.inp';
output='zero.out';
var s:integer;
x:array[1..100] of char;
n, dem:integer;
f1,f2:text;
procedure Init;
begin
assign(f1,input);reset(f1);
assign(f2,output);rewrite(f2);
readln(f1,n);
dem:=0;
s:=1;
end;
12


procedure print;
var i:integer;
begin
if s=0 then
begin
inc(dem);
write(f2,1);
for i:=2 to n do write(f2,x[i],i);

writeln(f2);
end;
end;
procedure try(i:integer);
var j:integer;
begin
if i>n then print else
for j:=1 to 2 do
begin
x[i]:=d[j];
case j of
1: s:=s+i;
2: s:=s-i;
end;
try(i+1);
case j of
1: s:=s-i ;
2: s:=s+i;
end;
end;
end;
BEGIN
Init;
try(2);
if dem=0 then writeln(f2,'Khong bieu thuc nao thoa man');
close(f1); close(f2);
END.
2.3. Chỉnh hợp không lặp
Khác với chỉnh hợp lặp là các thành phần được phép lặp lại, tức là có thể
giống nhau, chỉnh hợp không lặp chập k của tập n phần tử cũng là một dãy k thành

phần lấy từ tập n phần tử có xét thứ tự nhưng các thành phần không được phép
giống nhau.
13


Chẳng hạn có n người, một cách chọn ra k người để xếp thành một hàng là
một chỉnh hợp không lặp chập k của n.
Nghiệm của bài tốn tìm các chỉnh hợp không lặp chập k của tập n số nguyên
từ 1 đến n là các vector x thoả mãn các điều kiện:
1. x có k thành phần: x = (x1,x2,…xk)
2. Các giá trị xi lấy trong tập {1,2,..n}
3. Ràng buộc: các giá trị xi đôi một khác nhau, tức là xi≠ xj với mọi i≠ j.
Chỉnh hợp không lặp yêu cầu các phần tử phải khác nhau. Để đảm bảo điều
đó, ngồi mảng x, ta sẽ dùng thêm một cấu trúc dữ liệu nữa là mảng d để đánh dấu.
Khi một giá trị được chọn, ta đánh dấu giá trị đó, và khi chọn, ta chỉ chọn các giá trị
chưa đánh dấu. Mảng d sẽ là "trạng thái" của thuật toán. Bạn đọc xem phần giả mã
dưới đây để thấy rõ hơn ý tưởng đó.
procedure Try(i);
var j;
begin
for j := 1 to n do
if d[j]=0 then begin
x[i] := j; d[j] := 1;
if i=k then Print(x)
else Try(i+1);
d[i] := 0;
end;
end;
Ví dụ 1: Liệt kê các hoán vị của n số nguyên dương đầu tiên
*Phân tích

Nói một cách trực quan thì hốn vị của tập n phần tử là phép thay đổi vị trí của
n phần tử.
Ví dụ với N=3 ta có tập các hoán vị sau: 123, 132, 213, 231, 312, 321
* Chương trình
Program Hoanvi;
uses crt;
const
max = 20;
input='HOANVI.INP';
output='HOANVI.OUT';
var n : integer;
x,d : array[1..max] of integer;
f1,f2: text;
procedure readfile;
begin
14


clrscr;
assign(f1,input); reset(f1);
assign(f2,output); rewrite(f2);
readln(f1,n);
writeln(f2,'Cac hoan vi cua day ',n ,' so:');
end;
procedure print;
var
i : integer;
begin
for i := 1 to n do write(f2,x[i]);
writeln(f2);

end;
procedure try(i:integer);
var
j : integer;
begin
for j := 1 to n do
if d[j] = 0 then begin
x[i] := j; d[j] := 1;
if i = n then Print
else try(i+1);
d[j] := 0;
end;
end;
BEGIN
readfile;
try(1);
close(f1); close(f2);
END.
Cây tìm kiếm quay lui như sau:
Tr y ( 1 )

1

3

2
Tr y ( 2 )
2

3


Tr y ( 3 )
3

123

Tr y ( 2 )

Tr y ( 3 )

Tr y ( 2 )
3

1
Tr y ( 3 )

2

Tr y ( 3 )

213

Tr y ( 3 )

1

3

132


2

1

231

Tr y ( 3 )

2

312

1

321

15


Ví dụ 2:Bài tốn xếp hậu
Cho bàn cờ vua nxn. Hãy xếp n con hậu lên bàn cờ sao cho không con nào
khống chế con nào. Hai con hậu khống chế nhau nếu chúng ở trên cùng một hàng,
một cột hoặc một đường chéo.

*Phân tích
Để chuyển bài tốn này về dạng chuẩn của bài tốn tìm cấu hình tổ hợp, ta có
có nhận xét: mỗi con hậu phải ở trên một hàng và một cột. Do đó ta coi con hậu thứ
i ở hàng i và nếu biết x[i] là cột đặt con hậu thứ i thì ta suy ra được lời giải. Vậy
nghiệm của bài tốn có thể coi là một vector x gồm n thành phần với ý nghĩa:
1. Con hậu thứ i được đặt ở hàng i và cột x[i].

2. x[i] lấy giá trị trong tập {1,2…n}
3. Ràng buộc: các giá trị x[i] khác nhau từng đôi một và khơng có 2 con hậu ở
trên cùng một đường chéo.
Khác với những bài tốn sinh các cấu hình đơn giản ở phần trước, sinh các cấu
hình của bài tốn xếp hậu địi hỏi những phân tích chi tiết hơn về các điều kiện ràng
buộc.
Ràng buộc thứ nhất là các giá trị x[i] phải khác nhau. Ta có thể dùng một
mảng đánh dấu như ở thuật toán sinh hoán vị để đảm bảo điều này.
Ràng buộc thứ 2 là các con hậu không được nằm trên cùng một đường chéo
chính và phụ. Các bạn có thể dễ dàng nhận ra rằng 2 vị trí (x 1,y1) và (x2,y2) nằm
trên cùng đường chéo chính nếu:
x1−y1=x2−y2=const.
Tương tự, 2 vị trí (x1,y1) và (x2,y2) nằm trên cùng đường chéo phụ nếu:
x1+y1=x2+y2=const
Do đó, con hậu i đặt tại vị trí (i,x[i]) và con hậu j đặt tại vị trí (j,x[j]) phải thõa
mãn ràng buộc: i−x[i] ≠ j−x[j] và i+x[i] ≠ j+x[j] với mọi i≠ j
Ta có thể viết riêng một hàm Ok để kiểm tra các ràng buộc đó. Nhưng giải
pháp tốt hơn là dùng thêm các mảng đánh dấu để mô tả rằng một đường chéo chính

16


và phụ đã có một con hậu khống chế. Tức là khi ta đặc con hậu i ở vị trí (i,j), ta sẽ
đánh dấu đường chéo chính i-j và đường chéo phụ i+j.
Như vậy về cấu trúc dữ liệu, ta dùng 4 mảng:
- Mảng x với ý nghĩa: x[i] là cột ta sẽ đặt con hậu thứ i.
- Mảng cot với ý nghĩa: cot[j]=true nếu cột j đã có một con hậu được đặt,
ngược lại thì cot[j]=false
- Mảng dcc với ý nghĩa: dcc[k]=true nếu đường chéo chính thứ k đã có một
con hậu được đặt, tức là ta đã đặt một con hậu tại vị trí (i,j) mà i−j=k; ngược lại thì

dcc[k]=false.
- Tương tự ta dùng mảng dcp với ý nghĩa: dcp[k]=true nếu đường chéo phụ
thứ k đã có một con hậu được đặt.
* Chương trình
Program Queens;
const
InputFile = 'QUEENS.INP';
OutputFile = 'QUEENS.OUT';
max = 100;
var
n: Integer;
x: array[1..max] of Integer;
cot: array[1..max] of Boolean;
dcp: array[2..2 * max] of Boolean;
dcc: array[1 - max..max - 1] of Boolean;
f1,f2: Text;
procedure Init;
begin
Assign(f1, InputFile); Reset(f1);
Assign(f2, OutputFile); Rewrite(f2);
ReadLn(f1, n);
FillChar(cot, SizeOf(cot), false);
FillChar(dcp, SizeOf(dcp), false);
FillChar(dcc, SizeOf(dcc), false);
end;
procedure Print;
var
i: Integer;
begin
for i := 1 to n do Write(f2, '(', i, ', ', x[i], '); ');

WriteLn(f2);
end;
17


procedure Try(i: Integer);
var
j: Integer;
begin
for j := 1 to n do
if not cot[j] and not dcc[i-j] and not dcp[i+j] then
begin
x[i] := j;
cot[j]:=true; dcc[i-j]:=true; dcp[i+j]:=true; {ghi nhận trạng thái mới}
if i=n then print else Try(i+1);
cot[j]:=false; dcc[i-j]:=false; dcp[i+j]:=false ; {phục hồi trạng thái cũ}
end;
end;
BEGIN
Init;
Try(1);
Close(f1); Close(f2);
END.
Ví dụ 3. Tìm đường đi ngắn nhất:
Có n thành phố, a[i,j] là chi phí để di chuyển từ thành phố i đến thành phố j.
(Nếu khơng có đường đi thì a[i,j] = 0). Một người muốn đi du lịch qua tất cả các
thành phố, mỗi thành phố một lần rồi trở về nơi xuất phát sao cho tổng chi phí là
nhỏ nhất. Hãy xác định một đường đi như vậy.
*Phân tích
Phương án tối ưu của bài tốn cũng là một vector x, trong đó xi là thành phố sẽ

đến thăm tại lần di chuyển thứ i. Các điều kiện của x như sau:
1. x = (x1,x2,…xn)
2. xi lấy giá trị trong tập {1,2,…n}
3. Ràng buộc: xi ≠ xj với mọi i≠ j và a[xi,xi+1]>0 với mọi i=1,2,..n, coi xn+1=x1.
n

∑ a[ x , x
i

i +1

] → min

f(x) =
Trong đó:
- mảng x[i]: ghi lại hành trình
- Mảng a[i,j]: chi phí để đi từ thành phố i đến thành phố j
- Mảng d[i]: đánh dấu thành phố đã đi thăm, d[i]=true nếu đã đi thăm thành
phố i
Mỗi phương án của bài toán người du lịch là một hoán vị của n thành phố. Vì
phương án là một chu trình nên ta có thể coi thành phố xuất phát là thành phố 1.
* Chương trình
program Travelling;
i =1

18


const
Input = 'TOUR.INP';

Output = 'TOUR.OUT';
max = 100;
var
A: array[1..max, 1..max] of Integer;
X, Best : array[1..max + 1] of Integer;
D: array[1..max] of Boolean;
m, n: Integer;
Min: Integer;
procedure Readfile;
var
i, j, k: Integer;
f: Text;
begin
Assign(f, Input); Reset(f);
ReadLn(f, n, m);
for i := 1 to n do
for j := 1 to n do
a[i, j] := 0 ;
for k := 1 to m do
begin
ReadLn(f, i, j, a[i, j]);
a[j, i] := a[i, j] ;
end;
Close(f);
end;
procedure Init;
begin
FillChar(D, n, false);
d[1] := False;
X[1] := 1;

Min:= maxint;
end;
procedure Print;
var
i: Integer;
f: Text;
begin
Assign(f, Output); Rewrite(f);
19


for i := 1 to n do Write(f, Best[i], '->');
WriteLn(f, 1);
WriteLn(f, 'Hanh trinh ngan nhat: ', Min);
Close(f);
end;
procedure update;
var s,i:integer;
begin
s:=a[x[n],1];
for i:=1 to n-1 do s:=s+a[x[i],x[i+1]];
if sbegin
Min:=s;
best:=x;
end;
end;
procedure Try(i: Integer);
var
j: Integer;

begin
if i>n then update else
for j := 2 to n do
if not d[j] and (a[x[i-1],j]>0 ) then
begin
X[i] := j;
d[j] := true;
Try(i + 1);
d[j] := false;
end;
end;
BEGIN
Readfile;
Init;
Try(2);
Print;
END.
3. Một số bài toán tham khảo
Bài 1. Cho hình vng kích thước NX N (2≤ N≤ 5). Hãy điền các chữ cái
A,B,C,D vào các ô sao cho trên mỗi dòng cũng như mỗi cột mỗi chữ cái chỉ xuất
hiện một lần. Hỏi có bao nhiêu cách xếp
20


Bài 2. Bài toán mã đi tuần: Cho bàn cờ kích thước NxN. Tọa độ mỗi ơ của
bàn cờ là (x,y) với x là số hiệu dòng, y là số hiệu cột. Một quân mã đang ở ô (x, y),
hãy tìm một đường đi của quân mã sao cho quân mã đi qua tất cả các ô trên bàn cờ,
mỗi ô chỉ đi qua đúng một lần.
Bài 3. Cho N quả cân có các khối lượng tương ứng là q1, q2,…,qn (nguyên)
và một cái cân 2 đĩa. Khi cân có thể đặt một số quả cân và vật cần cân trên đĩa nào

cùng được sao cho cân thăng bằng. Cho vật có khối lượng M, hỏi có thể cân nó
bằng những quả cân nào?
Bài 4. Có M loại tiền có giá trị lần lượt là L1, L2,…, Lm (nguyên dương) với
số tờ mỗi loại là s1,s2,…sm (nguyên dương). Hãy tìm cách đổi tờ tiền có mệnh giá
X thành các loại tiền có trong kho
Bài 5. Bài tốn cái túi: Một nhà thám hiểm cần đem theo một cái túi có trọng
lượng khơng q b. Có n đồ vật cần đem theo. Đồ vật thứ j có trọng lượng là aj và
giá trị sử dụng là cj (j = 1, 2, 3, ..,n). Hỏi rằng nhà thám hiểm cần đem theo các đồ
vật nào để cho tổng giá trị sử dụng của các đồ vật đem theo là lớn nhất?
Bài 6. Một từ đợc gọi là chân chính loại M, N nếu nó đợc xây
dựng từ tập hợp gồm M ký tự, có độ dài N và không có 2 từ con
nào liên tiếp giống nhau.
Giả sử tập M={'1', '2', '3'}
Ví dụ: 1232; 2123; 1231 là những từ chân chính loại 3,4; còn
1123;1212;1233 là những từ không phải là từ chân chÝnh lo¹i
3,4
III. KẾT QUẢ THỰC NGHIỆM
Sau khi áp dụng đề tài trong q trình ơn thi đội tuyển, bằng hình thức giám
sát, ra đề kiểm tra kết hợp thực hành tôi đã thu được những kết quả sau:
N ội d u n g cầ n nắ m bắt
Số lượng
Hiểu được kiến thức vừa học
6/6
Tự viết lại chương trình đã được giới thiệu
6/6
Vận dụng kiến thức đã học để làm tốt các
4/6
bài tập

Tỉ lệ (%)

100%
100%
67%

21


C. KẾT LUẬN VÀ ĐỀ XUẤT
I. KẾT LUẬN
Phần lớn các bài toán liệt kê trong trường hợp tổng quát chỉ có phương pháp
tối ưu nhất là vét cạn. Tuy nhiên, nhược điểm của phương pháp này là độ phức tạp
tính toán rất lớn do hiện tượng bùng nổ tổ hợp. Trong một số trường hợp, để cải
tiến phương pháp này, người ta đặt các “mắt lọc” bằng cách loại bỏ hoặc chỉ chọn
một số hướng đi để việc tìm kiếm nhanh hơn. Do vậy đề tài này có thể phát triển
thêm bằng cách xét một số phương pháp cải tiên khác như kỹ thuật nhánh cận,
phương pháp quy hoạch động, phương pháp tham lam,…
Đề tài này là một phần kết quả của q trình nghiên cứu và ơn lun đội tuyển
của bản thân tôi trong những năm qua. Tôi mong rằng đề tài này có thể giúp ích
cho một số giáo viên cũng như học sinh trong q trình ơn luyện đội tuyển học sinh
giỏi.
Đề tài có thể cịn nhiều thiếu sót, mong được sự góp ý, bổ sung của các giáo
viên trong tổ bộ môn cũng như các giáo viên khác để đề tài có thể phát triển hồn
thiện hơn.
II. ĐỀ XUẤT, KIẾN NGHỊ
*Đối với cấp trường
- Cần trang bị màn hình tivi hoặc máy chiếu trên các phịng máy, các phòng
máy được kết nối Internet để học sinh khai thác thơng tin nhanh gọn hơn.
- Các máy tính trong phịng thực hành hiện nay đã cũ, cấu hình thấp và thường
hay hư hỏng, cần nâng cấp và đầu tư thêm số lượng máy để học sinh thực hành tôt
hơn

*Đối với cấp sở
- Cần tổ chức thêm một số cuộc thi thuộc lĩnh vực tin học, ví dụ như cuộc thi
Tin học trẻ, Tin học và nhà trường, Thi soạn thảo văn bản,… để tạo cơ hội cho các
em học sinh phát triển khả năng tin học.
- Cần quan tâm và đầu tư thêm cơ sở vật chất cho bộ mơn tin học như máy
chiếu đa năng, phịng thực hành,…
- Cần tăng thêm số lượng đi thi học sinh giỏi tỉnh nhằm mục đích mở rộng tìm
kiếm, khai thác các tài năng tin học trẻ

22


TÀI LIỆU THAM KHẢO
1.
2.
3.
4.

Gi ải t huật và l ập t rì nh( ebook) , t ác gi ả Lê M i nh Hoàng,
ĐH sư phạm Hà Nội
Phươ ng pháp gi ải bài t oán t rong t i n học, t ác gi ả Th.s
Trần Đức Huyên, NX B Gi áo dục
Em t ập l ập t rì nh 2, tác gi ả Trần Đỗ Hùng, NX B Gi áo
dục
Một số t ài li ệu t ham khảo qua i nt ernet

XÁC NHẬN CỦA THỦ TRƯỞNG ĐƠN VỊ

Thanh Hóa, ngày 20 tháng 5 năm 2021
Tôi xin cam đoan đây là SKKN của

mình viết, khơng sao chép của người
khác
Người viết
(Ký và ghi rõ họ tên)

Phạm Văn Cường

23



×