SỞ GIÁO DỤC VÀ ĐÀO TẠO THANH HÓA
TRƯỜNG THPT TRIỆU SƠN 2
SÁNG KIẾN KINH NGHIỆM
GIẢI CÁC DẠNG BÀI TOÁN LIỆT KÊ BẰNG PHƯƠNG
PHÁP VÉT CẠN SỬ DỤNG THUẬT TOÁN QUAY LUI
TRONG ÔN LUYỆN HỌC SINH GIỎI MÔN TIN HỌC
Người thực hiện: Phạm Thị Biên
Chức vụ: Giáo viên
SKKN thuộc bộ môn: Tin học
THANH HÓA NĂM 2014
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 đề........................................................................5
1. Một số khái niệm.........................................................................................5
2. Một số dạng bài toán liệt kê.........................................................................7
3. Một số bài toá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, ngoà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 toá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 toán khó và nắm vững
các phương pháp giải quyết bài toán đó.
Bài toán trong tin học thường rất đa dạng và phức tạp, mỗi bài toá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 toá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 toán đó.
Bài toán liệt kê là một trong những lớp bài toá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: “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 sử dụng thuật toán quay lui 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 Triệu Sơn 2 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, ngoà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.
Trong quá 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 hoàn thành đề tài.
3
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 hoàn toà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 toán, và viết những dòng lệnh
trên 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 quá 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 du n g cần n ắm bắt
Số lượng
Nắm đượ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 toán cấu hình 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.
Bài toán liệt kê gồm có 3 dạng: tổ hợp, chỉnh hợp lặp và chỉnh hợp không lặp.
Trong đó dạng bài toán chỉnh hợp không lặp thường hay xuất hiện nhiều nhất trong
các bài toán tin học
Sơ lược một số kiến thức đại số tổ hợp:
Cho S là một tập hữu hạn gồm n phần tử và số tự nhiên k. Gọi X là tập hợp
các số nguyên dương từ 1 đến k: X= {1, 2, …, k}
• 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.
Vậy số tổ hợp chập k của S gồm n phần tử là:
• 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.
Số chỉnh hợp lặp chập k của n phần tử là nk
• 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.
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.
Số chỉnh hợp không lặp chập k của n phần tử là: n(n-1)(n-2)…(n-k+1)=
Để giải được bài toán liệt kê cần xác định được thuật toá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 toán liệt kê, yê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.
5
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 toá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 toá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 toá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, xoá 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.
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 toá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ì
6
Begin
+ Lưu trạng thái của bài toá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 toá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;
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
7
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 toán vét cạn đối với bài toán tìm mọi cấu hình hay đếm
số cấu hình. Trong trường hợp bài toán cần tìm một cấu hình, tìm cấu hình tối ưu
thì thuật toá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 toá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 toán tìm cấu
hình tổ hợp, tối ưu tổ hợp
2. Một số dạng bài toán liệt kê (cấu hình tổ hợp)
Bài toán tổ hợp yê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 k phần tử: x=(x1,x2,…,xn)
2. Mỗi phần tử xi có thể nhận một trong tập các đối tượng a1,a2,…,an,
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 toán có thể yê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
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 toán tìm các tổ hợp của tập các số nguyên 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 toá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)
8
2. xi lấy giá trị trong tập {1,2,…n}
3. Ràng buộc: xi
Có 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
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;
9
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.
2.2. Chỉnh hợp lặp
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 toá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ố nguyên 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 hoàn toà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
10
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;
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
11
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:
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 toá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';
12
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;
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;
13
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
Nghiệm của bài toá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
đó, ngoà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ì hoá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';
14
output='HOANVI.OUT';
var n : integer;
x,d : 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 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.
15
Cây tìm kiếm quay lui như sau:
Try(1)
1
3
2
Try(2)
2
3
Try(3)
3
123
Try(2)
Try(3)
Try(2)
3
1
Try(3)
2
Try(3)
Try(3)
1
2
3
132
213
2
1
231
312
Try(3)
1
321
Ví dụ 2: Bài toá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 toán này về dạng chuẩn của bài toá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 toá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 toá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 toá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:
16
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
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;
17
procedure Print;
var
i: Integer;
begin
for i := 1 to n do Write(f2, '(', i, ', ', x[i], '); ');
WriteLn(f2);
end;
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 toá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
i =1
18
- 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;
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;
19
procedure Print;
var
i: Integer;
f: Text;
begin
Assign(f, Output); Rewrite(f);
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 s
begin
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);
20
Print;
END.
3. Mt s bi toỏn tham kho
Bi 1. Cho hỡnh vuụng kớch thc NX N (2 N 5). Hóy in cỏc ch cỏi
A,B,C,D vo cỏc ụ sao cho trờn mi dũng cng nh mi ct mi ch cỏi ch xut
hin mt ln. Hi cú bao nhiờu cỏch xp
Bi 2. Bi toỏn mó i tun: Cho bn c kớch thc NxN. Ta mi ụ ca
bn c l (x,y) vi x l s hiu dũng, y l s hiu ct. Mt quõn mó ang ụ (x, y),
hóy tỡm mt ng i ca quõn mó sao cho quõn mó i qua tt c cỏc ụ trờn bn c,
mi ụ ch i qua ỳng mt ln.
Bi 3. Cho N qu cõn cú cỏc khi lng tng ng l q1, q2,,qn (nguyờn)
v mt cỏi cõn 2 a. Khi cõn cú th t mt s qu cõn v vt cn cõn trờn a no
cựng c sao cho cõn thng bng. Cho vt cú khi lng M, hi cú th cõn nú
bng nhng qu cõn no?
Bi 4. Cú M loi tin cú giỏ tr ln lt l L1, L2,, Lm (nguyờn dng) vi
s t mi loi l s1,s2,sm (nguyờn dng). Hóy tỡm cỏch i t tin cú mnh giỏ
X thnh cỏc loi tin cú trong kho
Bi 5. Bi toỏn cỏi tỳi: Mt nh thỏm him cn em theo mt cỏi tỳi cú trng
lng khụng quỏ b. Cú n vt cn em theo. vt th j cú trng lng l aj v
giỏ tr s dng l cj (j = 1, 2, 3, ..,n). Hi rng nh thỏm him cn em theo cỏc
vt no cho tng giỏ tr s dng ca cỏc vt em theo l ln nht?
Bi 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. KT QU THC NGHIM
Sau khi ỏp dng ti trong quỏ trỡnh ụn thi i tuyn, bng hỡnh thc giỏm
sỏt, ra kim tra kt hp thc hnh tụi ó thu c nhng kt qu sau:
Ni du n g cn n m bt
S lng
Hiu c kin thc va hc
6/6
T vit li chng trỡnh ó c gii thiu
6/6
Vn dng kin thc ó hc lm tt cỏc
4/6
bi tp
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 quá trình nghiên cứu và ôn luyên độ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 quá 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 hoàn
thiện hơn.
II. ĐỀ XUẤT, KIẾN NGHỊ
*Đối với cấp trường
- Cần lắp đặt 2 đến 3 phòng máy chiếu cố định để giáo viên chủ động hơn
trong công tác giảng dạy,
- 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
T rầ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, NXB 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 06 tháng 4 năm 2014
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 Thị Biên
23