Tải bản đầy đủ (.docx) (25 trang)

SKKN sử dụng thuật toán lùa bò vào chuồng để giải các bài toán đếm

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 (288.46 KB, 25 trang )


I. ĐẶT VẤN ĐỀ
I.1. Lý do chọn đề tài
Sự phát triển như vũ bão của Công nghệ Thông tin và Truyền thơng đóng vai trị
khơng nhỏ trong sự phát triển chung của nhân loại. Đảng và nhà nước đã xác định rõ ý
nghĩa và tầm quan trọng của tin học, Công nghệ Thông tin và Truyền thông cũng như
yêu cầu đẩy mạnh của ứng dụng Công nghệ Thông tin, đào tạo thế hệ trẻ năng động,
sáng tạo, nắm vững tri thức khoa học công nghệ để làm chủ trong mọi hồn cảnh cơng
tác và hoạt động xã hội trong thời kỳ cơng nghiệp hóa và hiện đại hóa đất nước.
Chính vì xác định được tầm quan trọng đó nên nhà nước đã đưa môn tin học vào
trong nhà trường và ngay từ tiểu học học sinh được tiếp xúc môn tin học để làm quen
dần với lĩnh vực công nghệ thơng tin, tạo nền móng ban đầu để học những phần nâng
cao tiếp theo. Đối với các em học sinh, có thể nói đây là một hành trang để giúp các em
vững bước đi tới tương lai - tương lai của thời đại công nghệ thông tin bùng nổ!
Trong chương trình Tin học THPT lớp 10 học sinh được giới thiệu các kiến thức
đại cương về tin học, lớp 11 học sinh được giới thiệu về lập trình, lớp 12 học sinh được
học về cơ sở dữ liệu. Đặc biệt, chương trình Tin học lớp 11 là phần được cho là khó cho
Thầy Cơ giáo cũng như học sinh, vì phải làm thế nào để học sinh có thể hiểu được ngơn
ngữ lập trình, để từ đó có thể lựa chọn và thiết kế thuật tốn. Chương trình tin học lớp
11 nhằm rèn luyện tư duy về thuật toán cho học sinh, rèn luyện kĩ năng lập trình, tính
kiên trì, tỉ mỉ cẩn thận. Đối với học sinh thì phải làm quen với lối suy nghĩ logic với sự
hoạt động của máy tính, mà đây lại là một lối suy nghĩ hồn tồn khác với các mơn học
khác.
Từ thực tiễn giảng dạy học sinh đại trà cũng như học sinh đội tuyển học sinh giỏi
Tin học của trường THPT tôi thấy rằng, học sinh gặp khó khăn khi chuyển lời giải các
bài tốn từ tốn sang ngơn ngữ lập trình. Đặc biệt là việc phân tích bài tốn, nhận biết
bài tốn đó có thể giải quyết bằng phương pháp nào, cỏ lời giải tối ưu hay không?
Để hệ thống lại các chuyên đề bồi dưỡng học sinh giỏi (HSG) Tin học mà tôi đã
dạy trong nhiều năm qua, đồng thời qua quá trình nghiên cứu, giảng dạy, tham khảo ý
kiến đồng nghiệp, tơi thấy rằng một số bài tốn tin học có liên quan đến việc đếm.
Chính vì vậy tơi chọn viết đề tài về chuyên đề “Sử dụng thuật tốn “lùa bị vào


chuồng” để giải các bài tốn đếm”.
1


I.2. Mục tiêu nghiên cứu
"Đếm" là công việc quan trọng và đơn giản mà chúng ta làm thường ngày, điều đó
thì khơng cần phải bàn đến làm gì? Điều đáng nói ở đây là cơng việc có vẻ nhàm chán
đó lại chứa bao điều thú vị, nhất là khi gặp những bài toán yêu cầu ta phải "đếm".
"Đếm" ở đây không phải đơn thuần là đếm 1, 2, 3, ... mà còn cần ở người đếm một
sự khéo léo và có thể có một chút kỹ thuật. Bên cạnh đó để giúp các em học sinh có kiến
thức khoa học cơ bản, hiện đại, tiến tiến, có tính tự lập và khả năng sáng tạo, nhận thức
ở mức độ cao, tư duy tốt về lập trình. Các phương pháp đếm đơn giản là một trong
những vấn đề mà bất cứ người lập trình tin học đều cần phải nắm vững.
I.3. Nhiệm vụ nghiên cứu
Trước hết là thực hiện đổi mới phương pháp giảng dạy Tin học làm cho học sinh
tìm ra những kết quả sáng tạo, lời giải hay trên một số “dạng bài tốn tin có liên quan
đến việc đếm”; giúp bản thân nắm vững hơn nữa về tư duy thuật tốn, khả năng lập
trình, đồng thời để trao đổi và học tập kinh nghiệm với các quý thầy cơ giáo trong nhóm
Tin học của nhà trường nói riêng và các quý thầy cô giảng dạy môn Tin học trong ngành
nói chung.
I.4. Đối tượng nghiên cứu
Trong nghiên cứu này, các học sinh được chọn là các em học sinh học môn Tin
học khối 10, khối 11, các học sinh là thành viên của đội tuyển HSG môn Tin học, và
một số giáo viên đứng lớp dạy Tin học ở các trường THPT trên địa bàn.
I.5. Các phương pháp nghiên cứu
* Phương pháp suy luận, tổng hợp: kết hợp từ nhiều nguồn tài liệu tham khảo của

các tác giả và tra cứu trên mạng internet với các đề thi HSG rút ra những kinh nghiệm,
hệ thống lại kiến thức, mở ra các hướng mới.
* Phương pháp trò chuyện – phỏng vấn: trao đổi tâm tình với nhiều HSG để nắm


tình hình trong việc giải các bài tốn tin về dãy số (mảng).
* Phương pháp khảo sát: bản thân được tham gia giảng dạy các lớp, đội tuyển

HSG, các kỳ tập huấn; tham khảo các thầy cô, đồng nghiệp giảng dạy đội tuyển nhiều
năm nên có tìm hiểu thêm về các phương pháp làm bài của các em học sinh.
* Phương pháp phân tích lý luận: phân tích giúp học sinh nắm thật rõ bản chất vấn

đề, lựa chọn được phương pháp giải cho phù hợp.
2


AI. NỘI DUNG ĐỀ TÀI

II. 1. Lịch sử của vấn đề nghiên cứu
Trong những năm liên tiếp dạy bồi dưỡng HSG môn Tin Học lớp 10, 11, 12 thi
HSG cấp Tỉnh, cũng như tham khảo ý kiến các đồng nghiệp chuyên dạy bồi dưỡng đội
tuyển ở trong trường, trong ngành và các Tỉnh bạn, ở các trường THPT Chuyên, tôi rút
ra một điều là “công việc đếm rất quan trọng trong dạy lập trình”, vì vậy tơi mạnh dạn
chọn viết đề tài: Sử dụng thuật tốn “lùa bị vào chuồng” để giải các bài toán đếm.
II. 2. Cơ sở lý luận của đề tài
Kết hợp các bài giảng chuyên đề bồi dưỡng HSG của cá nhân tôi và các tài liệu
tham khảo để phân tích, tổng hợp, hệ thống.
II. 3. Thực trạng của vấn đề nghiên cứu
Đa số HSG tin rất ngại, sợ khi giải các bài tốn tin có liên quan đến việc đếm; rất
lúng túng trong quá trình phân tích, tổ chức dữ liệu, tìm thuật tốn hiệu quả để hiểu
được bản chất và vận dụng kiến thức một cách thích hợp.
AI. 4. Nội dung nghiên cứu và kết quả nghiên cứu

A. NỘI DUNG NGHIÊN CỨU

Tư tưởng của thuật toán được xây dựng dựa trên suy nghĩ thực tế là để đếm số
lượng bò trên một vùng xác định thì người ta phải tìm cách lùa chúng vào các chuồng
(để chúng khỏi chạy rông) cho dễ đếm. Đương nhiên là những con bò cùng loại phải
được lùa vào cùng một chuồng để dễ phân biệt hoặc lùa mỗi con vào một chuồng thì
càng tốt. Tương tự như vậy, muốn giải bài tốn đếm các đối tượng thì ta dùng một cấu
trúc dữ liệu hợp lý (thường dùng kiểu mảng hoặc kiểu con trỏ) với ý nghĩa giống như
các “chuồng” để lưu các đối tượng, mỗi phần tử của mảng tương ứng với một chuồng.
Trên cơ sở đó ta dễ dàng thực hiện được mục đích của mình là thực hiện thao tác đếm.
Để hiểu rõ hơn thuật toán ta xét ví dụ sau: “Viết chương trình nhập từ bàn phím
một xâu ký tự S và thơng báo ra màn hình số lần xuất hiện của mỗi chữ cái tiếng Anh
trong S (không phân biệt chữ hoa hay chữ thường)” – Bài 2. Bài tập và thực hành 5 sách Tin học 11 - trang 73. Rõ ràng với bài tốn này ta có thể duyệt tồn bộ xâu S và
mỗi lần duyệt ta thực hiện thao tác đếm một chữ cái. Sau 26 lần duyệt tương ứng với 26
chữ cái thì ta thu được tồn bộ kết quả. Tuy nhiên, vì việc duyệt xâu như vậy tương
3


đối chậm và có thể xâu văn bản đã cho rất dài nên về thời gian là không chấp nhận
được. Ta vận dụng thuật tốn “lùa bị vào chuồng” bằng cách sử dụng một mảng tĩnh
A:array[′A′..′Z′] of Longint; với ý nghĩa như sau: giá trị A[c] trong đó c thuộc [′A′..′Z′]
lưu số lần xuất hiện của ký tự c trong file văn bản. Mảng A được khởi tạo với tất cả các
giá trị bằng 0, khi duyệt xâu ta đọc lần lượt các ký tự trong xâu ra một biến trung gian
ch nào đó, và tăng giá trị của mảng tại vị trí tương ứng có chỉ số ch lên một đơn vị qua
câu lệnh: A[ch]:=A[ch] + 1. Như vậy, bài toán được giải quyết chỉ với một lần duyệt xâu
duy nhất.
Sau đây chúng ta cùng khảo sát một số ví dụ điển hình vận dụng thuật tốn này.
1. Bài 1: Đếm bị
Tên chương trình: DEMBO.*
Bài tốn đặt ra là giả sử trên cánh đồng rộng thả rất nhiều bò (N con), mỗi con bị
đeo một thẻ có số hiệu nguyên dương (là số tháng tuổi của nó).
Tất nhiên, hai con bị cùng tháng tuổi thì đeo thẻ có số hiệu như nhau. Làm thế nào

để đếm được loại bò nào có nhiều nhất?
Bài tốn này có thể phát biểu lại như sau:
+ Nhập từ bàn phím số nguyên dương N (0 < N ≤ 200) và các phần tử của mảng

một chiều A(N) có giá trị nguyên dương (0 < A[i] ≤ 100).
+ Giá trị nào xuất hiện nhiều nhất trong A và xuất hiện bao nhiêu lần?
+ Ví dụ: A(12) = {2, 3, 2, 4, 5, 6, 2, 6, 7, 1, 6, 2} thì A(12) có số phần tử giá trị

bằng 2 là nhiều nhất. Số lượng phần tử này là 4.
Thuật tốn “Lùa bị vào chuồng”
Để giải bài tốn này người ta dùng thuật tốn “lùa bị vào chuồng” gồm 3 bước: Bước 1: Đóng dãy chuồng bò và đánh số các chuồng bằng các số tự nhiên liên
tiếp từ 1 đến max (max là số tháng tuổi của con bò già nhất), ban đầu mọi chuồng đều
chưa có bị ở trong.
- Bước 2: Lùa bị vào chuồng có số hiệu bằng số thẻ của nó.
- Bước 3: Duyệt dãy chuồng bị tìm chuồng có nhiều bị nhất.
Áp dụng thuật toán vào bài tập:

4


+ Bước 1: Giả sử con bị thứ i có tháng tuổi là a[i] (1 ≤ i ≤ n). Coi mảng b(n) như

dãy chuồng bò, b[x] là số lượng bò (có x tháng tuổi) trong chuồng có số hiệu x, ban đầu
các phần tử của mảng b(n) đều bằng 0.
+ Bước 2: Con bị có tháng tuổi a[i] phải vào chuồng bị có số hiệu a[i]. Thêm 1

con bị vào chuồng a[i] tương ứng với lệnh inc(b[a[i]])
+ Bước 3: Duyệt mảng b, tìm chỉ số phần tử lớn nhất.

Chương trình tham khảo:

Code Pascal
Const max = 200;
Var N: integer;
A: array[1..max] of byte;
B: array[1..max] of byte;
maxsl, i, li: integer;
BEGIN
Write('Nhap N = '); Readln(N);
For i := 1 to N do
Begin
Write('A[', i, ' ] = '); Readln(A[i]);
End;
Fillchar(B, sizeof(B), 0); maxsl := 0;
{Tao day chuong bo rong chua co bo}
For i := 1 to N do inc(B[A[i]]);
{Tang them 1 con bo vao chuong co so hieu
A[i]}
For i := 1 to max do
{Duyet day chuong tim chuong co nhieu bo
nhat}
if B[i] > maxsl then
Begin
maxsl := B[i]; li := i;
End;
5


Write('So ', li, ' co so luong lon nhat la ',
maxsl);


return 0;
}

Readln;
END.
2. Bài 2: Nhập vào số

nguyên dương N (2 ≤
N ≤ 10000) và một dãy
a1, a2,….an các phần tử
nguyên dương (ai ≤
32000). Cho biết dãy
trên có bao nhiêu phần
tử khác nhau.
Ý tưởng: Để làm
bài này ta dùng thuật
tốn

“lùa

bị

vào

chuồng”, các phần tử
bằng nhau sẽ

nhốt

chung một chuồng, có

bao nhiêu chuồng thì
có bấy nhiêu phần tử
khác nhau trong dãy ban
đầu.
Ta

đóng

dãy

chuồng C, đánh số
chuồng lần lượt từ 1,
2….32000 ( ai là các số
nguyên

dương,

ai<32000), ban đầu các
chuồng

đều

trống).

Duyệt mảng A ban đầu,
phần tử a[i] sẽ được
“nhốt” vào chuồng có
số a[i] (tăng C[a[i]] lên
một đơn vị).. Đếm số



chuồng khác trống đó chính là số phần tử khác
nhau của dãy ban đầu.
Chương trình tham khảo:
Code Pascal
var A: array[1..10000] of integer;
C: array[0..maxint] of integer;
i, j, n, D: integer;
begin randomize;
write('Nhap N = '); readln(n);
for i:=1 to n do
a[i]:=random(maxint)-random(100);
writeln('Day phan tu la ');
for i:=1 to n do
write(a[i]:8);
fillchar(C, sizeof(C), 0);
for i:=1 to N do inc(C[a[i]]);
D:=0;
for j:=0 to maxint do
if c[j]<>0 then inc(D);

6


writeln('So phan tu khac nhau cua day la
', D:4);

cout << " so phan tu khac nhau la
"<return 0;


readln
end.

}
3. Bài 3. (Ví dụ 5. Tài liệu

chuyên Tin học - quyển 1 trang
53)
Cho dãy gồm N (N
<=30000) số tự nhiên không
9

vượt quá 10 , tìm số tự nhiên
nhỏ nhất khơng xuất hiện trong
dãy.
Dữ liệu vào trong file
SN.INP có dạng:
-

Dịng đầu là số nguyên
N

-

Dòng thứ hai gồm N số

Kết quả ra file SN.OUT có
dạng: số tự nhiên nhỏ nhất
khơng xuất hiện trong dãy.

SN.INP
5
50314
Ý tưởng: Ta có nhận xét

sau: số tự nhiên nhỏ nhất không
xuất hiện trong dãy sẽ nằm
trong đoạn [0, n]. Do đó, ta sử
dụng mảng c: array[0..30000]
of longint; với c[x] là số lần
xuất hiện của x trong dãy, nếu
c[x]=0 tức là x khơng xuất hiện
trong dãy.
Chương trình tham khảo:
Code Pascal
Const limit = 30000;


hoa hay chữ thường) trong văn

fi='SN.INP';

bản đã cho.

fo='SN.OUT';

Dữ liệu vào: từ file văn bản
có tên là FREQ.INP:

var c:array[0..Limit] of longint;

n, i, x: longint;

Dòng đầu tiên chứa N là số
lượng dòng văn bản.

f:text;
BEGIN

N dòng tiếp theo mỗi dòng
chứa một dòng của văn bản
đã cho.

fillchar(c, sizeof(c), 0);
assign(f, fi); reset(f); readln(f, n);

Kết quả: ghi ra file văn bản
có tên FREQ.OUT tần số
lớn nhất tìm được.

for i:=1 to n do
begin

Ví dụ:
7

read(f, x);
if x<=n then inc(c[x]);

FREQ.INP
4

faculty of technology

end;

Hanoi National University

close(f);

aaAAAAAaaaaAAAAaAaAaAa

for i:=0 to n do

cccCCCeeefffggg123456$#)(*+=

if c[i]=0 then begin
x:=i;
break;
end;
assign(f, fo); rewrite(f);
write(f, x);
close(f);
END.
4. Bài 4. Tần số (Đề thi Olimpic sinh viên

2001, khối đồng đội)
Cho một văn bản gồm khơng q N (N
≤500) dịng, mỗi dịng chứa khơng quá 80 ký
tự. Ta gọi tần số của một ký tự trong văn bản
là số lần xuất hiện của ký tự đó trong văn
bản.

u cầu: Tìm tần số lớn nhất trong các
tần số của các chữ cái (không phân biệt chữ

8


Lời giải cho bài tốn này gần giống như ví dụ đã trình bày ở trên. Bạn đọc có thể
xem thuật giải thơng qua chương trình mẫu dưới đây:
var a:array[1..256] of longint;
n:integer;
procedure solve;
var f:text;
i,j,k:integer;
s:string;
ss:set of byte;
begin
fillchar(a,sizeof(a),0);
ss:=[65..90];
assign(f,′FREQ.INP′); reset(f);
readln(f,n);
for i:=1 to n do
begin
readln(f,s);
for j:=1 to length(s) do
begin
k:=ord(s[j]);
if k in ss then k:=k+32;
inc(a[k]);
end;
end;

close(f);
end;
procedure output;
var f:text; i,m:longint;
begin
assign(f,′FREQ.OUT′);
Rewrite(f);
9


m:=a[1];
for i:=2 to 256 do
if m < a[i] then m:=a[i];
write(f,m); close(f);
end;
BEGIN
solve;
output;
END.
5. Bài 5. Phủ nhỏ nhất (Đề thi chọn HSG Quốc gia lớp 12 năm 1998-1999)

Cho tập hợp n đoạn thẳng (đánh số từ 1 đến n) với các đầu mút có toạ độ nguyên
[Li,Ri], i=1,2, 3,…,n và một đoạn thẳng [P,Q] (P, Q là các số nguyên).
Yêu cầu: Cần tìm một số ít nhất đoạn thẳng trong tập đã cho để phủ kín đoạn
thẳng [P,Q] (nghĩa là mỗi điểm x thuộc [P,Q] phải thuộc vào ít nhất một trong số các
đoạn thẳng được chọn).
Dữ liệu vào: từ file văn bản PHU.INP:
- Dòng đầu tiên ghi 3 số n, P, Q phân cách nhau bởi dấu trắng;
- Dòng thứ i trong số n dòng tiếp theo chứa hai số L, R phân cách nhau bởi dấu trắng


(i=1, 2,…, n); 1 ≤ n ≤ 100 000; P − Q ≤ 5000; |Li| ≤ 50000; |Ri|≤50000,
i = 1, 2,…, n.
Kết quả: ghi ra file văn bản PHU.OUT:
- Dòng đầu tiên ghi số k là số lượng đoạn cần chọn (quy ước ghi số 0 nếu khơng tìm

được lời giải);
- Nếu k > 0 thì mỗi dịng trong số k dịng tiếp theo ghi số của một đoạn thẳng được

chọn.
Ví dụ:
PHU.INP
201
-1 0
01

10


Thuật tốn: Ta phân tích bài tốn để có thể áp dụng sáng tạo tư tưởng của thuật
tốn đã trình bày như sau: Ta thấy rằng độ dài đoạn thẳng [P,Q] khơng lớn hơn 5000.
Cịn số đoạn thẳng trong tập đã cho có thể lên tới 100000. Ta khơng thể lưu tất cả các
đoạn thẳng này lại được. Vậy chúng ta có thể lưu các đoạn thẳng này theo cách: lưu
từng đoạn một, dùng một mảng A có độ dài chính bằng độ dài đoạn thẳng [P,Q], kích
thước của mảng này là 5000x2 (A: Array [ 0..5000, 1..2 ] Of LongInt;)
Ta coi đoạn [P,Q] là mảng A trên. Ta lần lượt đọc các đoạn thẳng của tập n các đoạn
thẳng. Với mỗi đoạn thẳng vừa đọc được, ta xét xem chúng phủ đoạn [P,Q] bắt đầu từ
đâu và độ dài mà đoạn thẳng đó phủ được. Ta sẽ lưu lại trên mảng A ở phần tử mà đoạn
thẳng chúng ta vừa đọc bắt đầu phủ đoạn [P,Q] chỉ số của đoạn thẳng đó và độ dài
chúng phủ được.
A[Phần tử bắt đầu phủ,1] = chỉ số của đoạn vừa đọc và A[Phần tử bắt đầu

phủ, 2] = độ dài mà đoạn đó phủ được.
Nếu có nhiều đoạn thẳng bắt đầu phủ đoạn [P,Q] tại cùng một vị trí thì chọn đoạn
nào có độ dài phủ được là lớn nhất.
Sau khi làm xong công việc trên ta được một mảng gồm các đoạn thẳng phủ lên
đoạn [P, Q]. Bây giờ ta sẽ tiến hành tìm xem số đoạn phủ ít nhất sẽ là bao nhiêu bằng
cách: ta bắt đầu đi từ vị trí 0 (nếu tại vị trí này mà khơng có đoạn nào phủ thì sẽ khơng
có lời giải) ta tìm xem có đoạn nào giao với đoạn này mà tạo thành một đoạn thẳng có
phủ dài nhất thì ta lưu đoạn này lại (nếu khơng có đoạn thẳng nào giao với đoạn thẳng
này để tạo được một đoạn thẳng có độ che phủ lớn hơn độ che phủ của đoạn trên thì sẽ
khơng có lời giải). Làm tương tự với đoạn thẳng vừa tìm được cho đến khi các đoạn
thẳng được chọn sẽ phủ kín đoạn [P, Q] ta sẽ được số đoạn thẳng ít nhất để phủ đoạn [P,
Q]. Như vậy, thuật toán này cho phép giải quyết bài toán cũng chỉ với một lần duyệt
mảng duy nhất.
Dưới đây là chương trình minh hoạ:
Const

Fi = ′PHU.INP′;
Fo = ′PHU.OUT′;
Max = 5000; Dd = 2;

Var

n, P, Q, Min, Id : LongInt;
A : Array [ 0..Max,1..2 ] Of LongInt;
11


C: Array[0..Max] Of LongInt;

F : Text;

Procedure InitData;
Var i, L, R : LongInt;
Begin
Id:=1;
Assign(F, Fi); Reset(F);
Readln(F, n, P, Q);
FillChar(A, SizeOf(A), 0);
For i := 1 To n Do
Begin
Readln(F, L, R);
If L <= P then
If A[0, Dd] < R - P then
Begin
A[0, id] := i;
If R > Q then A[0, Dd] := Q - P else A[0, Dd] := R - P;
End
else
else If L <= Q then
If A[L - P, Dd] < R - L then
Begin
A[L - P, id] := i;
If R > Q then A[L-P, Dd]:= Q-L else A[L-P, Dd]:= R - L;
End;
End;
Close(F);
End;
Function tt(u,v:LongInt):LongInt;
Begin
If A[u, Dd]+u<=A[v, Dd]+v then tt:=A[v, Dd]+v else tt:=A[u, Dd]+u;
12



End;
Procedure solve;
Var

i, j, tmp, Count, Max:LongInt;
Dung:Boolean;

Begin
Min:=0;
If A[0, id]=0 then Exit;
j:=0; Dung:=False; Count:=1;
C[1]:=A[0, id];
If A[0, Dd] = Q-P then
Begin
Min:=1; Exit;
End;
While Not Dung Do
Begin
Max:=j+A[j, Dd];
For i:=j + 1 To j + A[j, Dd] Do
If (A[i, id]<>0) And (tt(i, j)>Max) then
Begin
Max:=tt(i, j); tmp:=i;
End;
If Max=j+A[j, Dd] then Exit else
Begin
Count:=Count + 1;
C[Count]:=A[tmp, id];

j :=tmp;
If Max = Q - P Then Dung := True;
End;
End;
Min := Count;

13


End;
Procedure ReSult;
Var

i : LongInt;

Begin
Assign(F, FO); Rewrite(F);
Writeln(F, Min);
If Min <> 0 Then
For i: = 1 To Min Do Writeln(F, C[i]);
Close(F);
End;
BEGIN
InitData;
solve;
ReSult;
END.
Tham khảo thêm: Trong Sáng tạo trong thuật tốn và lập trình - tập 2 (Ebook)
tác giả Đỗ Xuân Huy có giới thiệu thêm thuật toán giải bài toán này như sau:
2


Bài 1.7 trang 21 - Phương pháp: Tham (độ phức tạp N )
* Sắp các đoạn tăng theo đầu phải R.
* k : = 1; { chỉ số đầu tiên }; v := P; { Đầu trái của đoạn [P,Q] }
* Lặp đến khi v >= Q
- Duyệt ngược từ N đến k
- Tìm đoạn j [Lj, Rj] đầu tiên có đầu trái Lj <=v
+ Nếu khơng tìm được: vơ nghiệm;
+ Nếu tìm được:

Ghi nhận đoạn j;
Đặt lại v := Rj;
Đặt lại k := j+1;

14


* Có thể dùng thuật tốn “Lùa bị vào chuồng” để giải quyết bài tốn sau:

1. Bài 1: Dự tiệc
Ơng An đến dự một buổi tiệc. Buổi tiệc đã có N người (0 < N < 500.000). Mỗi
người dự tiệc đều được cài lên áo một bơng hoa trên đó có ghi một con số ngun
dương X bất kì ( X<106) cho biết người khách đó sẽ dự tiệc tại phịng có chỉ số X. Hầu
hết các phịng đều có số lượng khách là số chẵn, duy nhất chỉ có một phịng có số lượng
khách là số lẻ.
Để đảm bảo đủ cặp cho việc khiêu vũ nên ban tổ chức cần tìm ra số hiệu của
phịng khách có số lượng khách là số lẻ để ghi số cho ông An.
Yêu cầu: Cho trước một danh sách khách dự tiệc cùng với các số trên áo của họ,
hãy giúp ban tổ chức tìm ra số hiệu của phịng khách có số lượng khách là số lẻ.
Dữ liệu vào: Cho trong file văn bản DUTIEC.INP, gồm:

+ Dòng đầu tiên ghi một số N cho biết số khách của buổi tiệc khi ông An đến.
+ Trong N dòng tiếp theo, mỗi dòng ghi một số nguyên dương cho biết con số ghi

trên áo của người khách thứ i.
Dữ liệu ra: Ghi ra file văn bản DUTIEC.OUT gồm một số nguyên dương duy
nhất. Đó là số hiệu của phịng có số lượng khách là số lẻ.
Ví dụ:
DUTIEC.INP
5
1
2
2
3
1
Hướng dẫn: Trong bài tốn này, dãy phịng chỉ cần đóng số hiệu từ 1 đến số hiệu
6

Xmax (Xmax = 10 ) và mỗi phòng chỉ chứa 1 trong 2 trạng thái chẳn hoặc lẻ (true,
false), tìm thấy thêm một người khách của phịng i thì đổi trạng thái phịng i (tốn tử
not).

15


2. Bài 2: Chuỗi kiểm tra
Cho một file văn bản có n dịng (3 < n ≤ 30000), mỗi dịng là một chuỗi S có tối đa
255 kí tự, các kí tự S[i] ∈ [‘a’..’z’] với 1 ≤ i ≤ length(S). Trong đó, chỉ có một chuỗi S
có số lần xuất hiện là một số lẻ, các chuỗi khác có số lần xuất hiện là số chẳn.
Yêu cầu: Tìm chuỗi S (có số lần xuất hiện lẻ) đó.
Dữ liệu vào: từ file ‘CHUOIKT.inp’

+Dòng đầu là một số nguyên n.
+ Dòng thứ i + 1 trong n dòng sau, mỗi dòng là một chuỗi kí tự (1 ≤ i ≤ n).
Kết quả: ghi vào file ‘CHUOIKT.out’ chứa chuỗi kí tự tìm được.
Ví dụ:
CHUOIKT.INP
7
abcdef
bbcc
lop10tin
abcdef
bbcc
abcdef
abcdef
3. Bài 3: Tuyển nhân viên
Cơng ty phần mềm máy tính A có số lượng nhân viên rất lớn. Để tiện việc quản lý,
công ty đã cấp cho mỗi nhân viên một mã số, mã số của mỗi nhân viên là một số nguyên
dương, hai nhân viên bất kỳ thì có mã số khác nhau. Tuy nhiên, sau một thời gian thì
một số nhân viên đã nghỉ hưu hoặc chuyển công tác, nên công ty phải tiến hành tuyển
thêm k nhân viên mới. Các nhân viên mới này sau khi được tuyển vào cũng sẽ được cấp
mã số, mỗi nhân viên một mã số và mã số này cũng phải là một số nguyên dương.
Yêu cầu: Với n nhân viên hiện có (cịn lại) của cơng ty tương ứng với các mã số
a1, a2, …, an. Hãy tìm k mã số nhỏ nhất để cấp cho k nhân viên mới tuyển vào sao cho
vẫn thỏa mãn hai nhân viên bất kỳ (cả nhân viên cũ và nhân viên mới) có mã số khác
nhau.
16


Dữ liệu vào: từ file ‘Recruit.inp’ có nội dung như sau:
6


+ Dòng đầu chứ hai số nguyên dương lần lượt là n và k (k ≤ n ≤ 10 ).
9

+ n dòng tiếp theo, dòng thứ i là số nguyên dương ai (i = 1, 2, …, n; ai≤ 2*10 ).

Kết quả: ghi vào file ‘Recruit.out’ k mã số theo thứ tự từ nhỏ đến lớn (mỗi mã số
trên một dịng).
Ví dụ:
RECRUIT.OUT

RECRUIT.INP
2

53

3

4

1

5

6
9
8
Hướng dẫn:
+ Cách 1: Thuật tốn O(NlogN) dùng quicksort đủ chấp nhận.
+ Cách 2: Dùng Sắp xếp phân phối (Distribution sort). Trong bài tốn này, dãy


chuồng chỉ cần đóng số hiệu từ 1 đến số hiệu 2*nmax (nmax = 10 6) và mỗi chuồng chỉ
chứa 1 trong 2 trạng thái đã có hoặc chưa có (true, false). Bằng cách chạy từ 1 đến n,
nếu số nào < n+k thì đánh dấu chuồng đó bằng true. Sau đó chạy từ 1 đến n+k, nếu số
nào bằng false thì in số đó ra file (chỉ in đủ k số).
4. Bài 4: Xâu fibonacci.
Định nghĩa: Dãy xâu fibo được xây dựng theo nguyên tắc sau:
+ F1 =’B’; F2 =’A’;
+ Fk = Fk-1 + Fk-2.

Yêu cầu: Tính số lần xuất hiện của SR trong F n (tức là số xâu con các kí tự liên
tiếp nhau bằng SR). Hai xâu con được gọi là khác nhau nếu khác nhau ít nhất một ký tự.
(N<=100).
Dữ liệu vào: File FSTR.inp
+ Dòng đầu tiên là số N.
+ Dòng tiếp theo là xâu SR.(length(SR)<=15).

Dữ liệu ra: File FSTR.out
Số lần xuất hiện tìm được.
17


Hướng dẫn Thuật toán: Với phương pháp này chỉ giải quyết với dữ liệu không lớn
lắm (N<=35) nhưng cũng là một cách để cách bạn tham khảo.
+ Tìm Fn. Ta sẽ tìm Fn và đưa đó vào file để chứa.

Chuơng trình tìm và xuất Fn rất đơn giản như sau:
Function Fibo(N:integer):integer;
Begin
If n=1 then write(g, ’B’)
Else

If n=2 then write(g, ’A’)
Else
Fibo:=Fibo(n-2)+Fibo(n-1);
End;
+ Nhập xâu SR. Ta tưởng tượng rằng ’A’ chính là 1,’B’ chính là 0. Lúc này SR là

biểu diễn nhị phân của số K. Vấn đề tìm k khá đơn giản. Sau khi tìm được k, ta mở file
chứa Fn ra, sau đó lấy từng đoạn liên tiếp có chiều dài length(SR) ra chuyển ra nhị phân
(với ’A’ chính là ’1’, ’B’ là ’0’), nếu chuyển ra nhị phân bằng đúng k thì tăng đếm lên 1.
Sau khi đọc hết file thì biến đếm chính là kết quả cần tìm.
Ví dụ:
SR=’ABA’ SR=’101’ đây là biểu diễn của số K=5.
Nếu dịch từng đoạn 3 ta sẽ đươc các xâu sau:
’101’, ’011’,’110’,’101’,’010’,’101’,’011’,’110’,’101’,’011’,’110’. Ta sẽ chuyễn
các xâu này ra số nếu bằng k thì tăng dem lên. Để tiết kiệm thời gian ta sẽ dùng bit để
khỏi dùng chương trình chuyển nhị phân.
+ Có thể có bạn sẽ hỏi là tại sao ta khơng dùng xâu ln cho nó nhanh khỏi phải

dùng nhị phân chi cho nó mệt. Ta đọc từng đoạn xâu con rồi kiểm tra có bằng SR khơng
là xong. Tơi muốn giới thiệu cho các bạn phương pháp trên để giải quyết bài có thể hỏi
nhiều xâu Sr chứ khơng phải là một xâu. Nếu như dùng xâu thì ta tốn đến 15 byte,
nhưng dùng số thì chỉ tốn 2 byte thơi.
B. KẾT QUẢ NGHIÊN CỨU
Qua q trình nghiên cứu và vận dụng đề tài chuyên đề “Sử dụng thuật toán “lùa
bị vào chuồng” để giải các bài tốn đếm”, tơi nhận thấy vấn đề này giúp ích rất nhiều
cho HSG môn Tin học trong việc học, giúp các em không còn “e ngại” chuyên
18


đề này nữa, các em đã hiểu và vận dụng khá tốt những phần liên quan đến việc đếm ứng

dụng vào Tin học; một số em đã bước đầu sáng tạo được những cách giải hay, các giải
mới (tuy là những bài tốn cịn “đơn giản”). Riêng bản thân tơi sẽ tiếp tục nghiên cứu
sâu hơn nữa về chuyên đề này hy vọng sẽ “làm rõ hơn nữa” để các học sinh trong đội
tuyển HSG Tin học thích học và đạt nhiều thành tích hơn nữa.
Nếu biết vận dụng tốt những suy luận sẽ làm cho những bài toán tin có những giải
thuật đơn giản và đạt được kết quả tốt hơn. Đặc biệt là trong cơng việc đếm, địi hỏi phải
lựa chọn cách giải quyết phù hợp cho các bài tốn có đầu vào lớn.

19


III. KẾT LUẬN VÀ KIẾN NGHỊ
1. Kết luận
Công tác dạy và bồi dưỡng HSG ở trường tôi những năm gần đây tuy chưa đạt
được kết quả cao trong kỳ thi HSG Tỉnh, nhưng phần nào đã đáp ứng được yêu cầu rèn
luyện các mục tiêu nhận thức ở mức độ cao, sự trưởng thành của học sinh sau khi rời
ghế nhà trường phổ thông. Hầu hết các em HSG môn Tin học đều được học tập ở môi
trường cao hơn và học giỏi ở các trường Đại học.
So với số lượng tài liệu của những mơn học truyền thống thì tài liệu để học chuyên
tin thật sự quá ít. Chủ yếu các tài liệu chỉ nêu đề bài, rồi cho code, sách nào kỹ nữa thì
cho test chứ những tài liệu mang tính định hướng làm bài, phân tích thuật tốn rất ít. Vì
vậy, tơi viết đề tài nghiên cứu này nhằm mục đích cùng trao đổi với Quý thầy cô dạy bồi
dưỡng HSG Tin học về chuyên đề “Sử dụng thuật tốn “lùa bị vào chuồng” để giải
các bài toán đếm” để “hệ thống” các kiến thức, một vài kỹ năng, ứng dụng thuật tốn
vào lập trình giải quyết các bài tốn tin.
2. Kiến nghị, đề xuất
Trong q trình áp dụng chuyên đề, để chuyên đề thực sự có hiệu quả chúng ta cần
lưu ý những vấn đề sau:
- Bồi dưỡng học sinh giỏi là một quá trình mang tính khoa học, khơng thể chỉ một


vài tháng mà phải xây dựng kế hoạch cụ thể trong suốt cả ba năm học; như vậy mới
cung cấp được đầy đủ các kiến thức cần thiết cho học sinh. Các bài giảng dạy theo các
chuyên đề, tách thành các chủ đề, chủ điểm càng cụ thể càng tốt.
- Các giáo viên chúng ta cũng phải bình tĩnh, khơng nóng vội; nên đặt ra những

mục tiêu giảng dạy thật thấp trong các bài học đầu tiên để đảm bảo học sinh không bị
chán nản.
- Việc gia tăng các tài liệu cho học sinh giỏi là việc tất yếu. Tài liệu viết nên có

tính hệ thống hơn, nên biên soạn các bài tập theo chủ đề từ dễ đến khó, để kể cả các học
sinh và giáo viên mới tiếp cận cũng dễ nắm bắt.
Với những đặc điểm nêu trên, tôi kết luận rằng chuyên đề này có thể áp dụng được
rộng rãi ở các trường THPT trong công tác bồi dưỡng HSG môn Tin học. Mong rằng,
chuyên đề sẽ được sự góp ý của Hội đồng khoa học, các đồng nghiệp tham khảo
20


và bổ sung đầy đủ hơn. Vì kiến thức và thời gian còn nhiều hạn chế nên chắc rằng
nghiên cứu cịn có thiếu sót, tơi chân thành đón nhận sự góp ý của Q thầy cơ.
Xin chân thành cảm ơn!

21


TÀI LIỆU THAM KHẢO
1. Hồ Sĩ Đàm (Chủ biên) 2006, Tin học 10, NXB Giáo dục, Hà Nội.
2. Hồ Sĩ Đàm (Chủ biên) 2006, Tin học 10 - Sách giáo viên, NXB Giáo dục, Hà Nội
3. Hồ Sĩ Đàm (Chủ biên) 2007, Tin học 11, NXB Giáo dục, Hà Nội
4. Hồ Sĩ Đàm (Chủ biên) 2007, Tin học 11 – Sách giáo viên, NXB Giáo dục, Hà Nội
5. Hồ Sĩ Đàm (Chủ biên) 2011, Tài liệu chuyên Tin học - quyển1, NXB Giáo dục, Hà Nội

6. Lê Minh Hoàng, Giải thuật và lập trình (Ebook-Bài giảng chuyên đề), ĐHSP Hà Nội
7. Nguyễn Xuân Huy, 2008, Sáng tạo trong thuật toán và lập trình - tập 1 (Ebook).
8. Nguyễn Xuân Huy, 2010, Sáng tạo trong thuật tốn và lập trình - tập 2 (Ebook).
9. Nguyễn Xuân Huy, 2010, Sáng tạo trong thuật tốn và lập trình - tập 3(Ebook).
10. Một số tài liệu trên mạng Internet;
11. Các đề thi HSG cấp tỉnh, các đề thi HSG Quốc gia, Các đề thi HSG Quốc tế.

22


MỤC LỤC
Trang
I. ĐẶT VẤN ĐỀ................................................................................................................................. 1
I.1. Lý do chọn đề tài......................................................................................................................... 1
I.2. Mục tiêu nghiên cứu................................................................................................................... 2
I.3. Nhiệm vụ nghiên cứu................................................................................................................. 2
I.4. Đối tượng nghiên cứu................................................................................................................. 2
I.5. Các phương pháp nghiên cứu................................................................................................... 2
II. NỘI DUNG ĐỀ TÀI..................................................................................................................... 3
II. 1. Lịch sử của vấn đề nghiên cứu.............................................................................................. 3
II. 2. Cơ sở lý luận của đề tài........................................................................................................... 3
II. 3. Thực trạng của vấn đề nghiên cứu........................................................................................ 3
II. 4. Nội dung nghiên cứu và kết quả nghiên cứu...................................................................... 3
A. NỘI DUNG NGHIÊN CỨU................................................................................................... 3
B. KẾT QUẢ NGHIÊN CỨU................................................................................................... 18
III. KẾT LUẬN VÀ KIẾN NGHỊ................................................................................................ 20
1. Kết luận...................................................................................................................................... 20
2. Kiến nghị, đề xuất.................................................................................................................... 20
TÀI LIỆU THAM KHẢO.............................................................................................................. 22


23


×