Sáng kiến kinh nghiệm Sử dụng thuật toán “lùa bò vào chuồng” để giải các bài toán đếm

docx 24 Trang tailieuthpt 88
Bạn đang xem 20 trang mẫu của tài liệu "Sáng kiến kinh nghiệm Sử dụng thuật toán “lùa bò vào chuồng” để giải các bài toán đếm", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.

Tóm tắt nội dung tài liệu: Sáng kiến kinh nghiệm Sử dụng thuật toán “lùa bò vào chuồng” để giải các bài toán đếm

Sáng kiến kinh nghiệm Sử dụng thuật toán “lùa bò vào chuồng” để giải các bài toán đếm
 z
 SỞ GIÁO DỤC VÀ ĐÀO TẠO HÀ TĨNH
 
 SÁNG KIẾN KINH NGHIỆM
 Đề tài: 
 Sử dụng thuật toán “lùa bò vào chuồng” 
 để giải các bài toán đếm
 Hà Tĩnh, tháng 09 năm 2019 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 toá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 toá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 toá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 đố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 toá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 toán này.
1. Bài 1: Đếm bò 
 Tên chương trình: DEMBO.*
 Bài toá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 toá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 toán “Lùa bò vào chuồng”
 Để giải bài toán này người ta dùng thuật toá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 Write('So ', li, ' co so luong lon nhat la ', return 0;
maxsl); }
 Readln; 
 END.
2. Bài 2: Nhập vào số nguyên dương N (2 ≤ N ≤ 10000) và một dãy a 1, 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 toá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 ( a i 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 Code C++
var A: array[1..10000] of integer; #include 
 C: array[0..maxint] of integer; using namespace std;
 i, j, n, D: integer; const int maxint = 32768;
begin randomize; int i, D, n, a[10000], c[maxint];
 write('Nhap N = '); readln(n); int main()
 for i:=1 to n do { cout >n;
 a[i]:=random(maxint)-random(100); for(i=1; i<=n; i++)
 writeln('Day phan tu la '); a[i]=2+rand()%10001;
 for i:=1 to n do memset(c, 0, sizeof(c));
 write(a[i]:8); for(i=1; i<=n; i++)
 fillchar(C, sizeof(C), 0); { cout<<a[i]<<endl;
 for i:=1 to N do inc(C[a[i]]); c[a[i]]++; }
 D:=0; D=0;
 for j:=0 to maxint do for (i=1;i<= maxint;i++)
 if c[j]0 then inc(D); if (c[i]>0) D++;
 6 read(f, x); if(x<=n) c[x]++;
 if x<=n then inc(c[x]); }
 end; for (i=1; i<=n; i++)
 close(f); if(c[i]==0)
 for i:=0 to n do {
 if c[i]=0 then begin x=i;
 x:=i; break;
 break; }
 end; freopen("SN.out", "w", stdout);
 assign(f, fo); rewrite(f); cout<<x;
 write(f, x); return 0;
 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 quá 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.
 Yê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ữ hoa hay chữ thường) trong văn bản đã cho.
 Dữ liệu vào: từ file văn bản có tên là FREQ.INP:
 Dòng đầu tiên chứa N là số lượng dòng văn bản.
 N dòng tiếp theo mỗi dòng chứa một dòng của văn bản đã cho.
 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.
 Ví dụ: 
 FREQ.INP FREQ.OUT
 4 25
faculty of technology
Hanoi National University
aaAAAAAaaaaAAAAaAaAaAa
cccCCCeeefffggg123456$#)(*+= 
 8 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 PHU.OUT
 2 0 1 1
 -1 0 2
 0 1
 10 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 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 toá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: 
Bài 1.7 trang 21 - Phương pháp: Tham (độ phức tạp N2)
* 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 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 CHUOIKT.OUT 
 7 lop10tin 
 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 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 luôn 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 quá 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 toá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 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 toá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 toá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 toán vào lập trình giải quyết các bài toán tin. 
 2. Kiến nghị, đề xuất
 Trong quá 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 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 toán và lập trình - tập 2 (Ebook).
9. Nguyễn Xuân Huy, 2010, Sáng tạo trong thuật toá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

File đính kèm:

  • docxsang_kien_kinh_nghiem_su_dung_thuat_toan_lua_bo_vao_chuong_d.docx