Lí do chọn đề tài Hiện nay tài liệu dạng chuyên đề nâng cao phục vụ cho việc bồi dưỡng họcsinh giỏi môn Tin học là chưa thật sự đầy đủ và tổng quát, đặc biệt là nhữngchuyên đề khá mới và
Trang 1PHẦN 1 MỞ ĐẦU 1
I Lí do chọn đề tài 1
II Mục đích của đề tài 1
III Đối tượng và phạm vi nghiên cứu 1
IV Phương pháp nghiên cứu 1
PHẦN 2 NỘI DUNG SÁNG KIẾN KINH NGHIỆM 2
I Cơ sở lý luận 2
I.1 Bài toán tìm kiếm 2
I.2 Thuật toán tìm kiếm nhị phân 2
I.3 Hiệu quả của tìm kiếm nhị phân trong các bài toán tìm kiếm 2
II Vận dụng thuật toán giải quyết tối ưu một số bài toán .4
II.1 Một số dạng bài sử dụng thuật toán tìm kiếm nhị phân 4
II.2 Một số bài toán tìm kiếm nhị phân cơ bản 9
II.3 Một số bài toán nâng cao sử dụng thuật toán tìm kiếm nhị phân 12
III Hiệu quả của sáng kiến 18
PHẦN 3 KẾT LUẬN 19
Trang 2PHẦN 1 MỞ ĐẦU
I Lí do chọn đề tài
Hiện nay tài liệu dạng chuyên đề nâng cao phục vụ cho việc bồi dưỡng họcsinh giỏi môn Tin học là chưa thật sự đầy đủ và tổng quát, đặc biệt là nhữngchuyên đề khá mới và rất khó, giờ đã được đưa vào các đề thi học sinh giỏi tỉnhnhư: Chia để trị, Quy hoạch động, …
Từ thực tiễn giảng dạy tin tại trường THPT tôi thấy rằng để đạt hiệu quả caotrong bồi dưỡng học sinh giỏi, cần có cách thiết kế bài giảng cho phù hợp với nộidung kiến thức dựa trên tài liệu chuyên đề đầy đủ rõ ràng từ lýthuyết đến bài tập
và theo đúng xu thế chung của các kì thi học sinh giỏi các cấp hiện nay và trong tươnglai, để mang lại thành tích cao cho đội tuyển học sinh giỏi trong các kì thi
Xuất phát từ cơ sở trên, tôi chọn đề tài “ Vận dụng thuật toán tìm kiếmnhị phân vào giải quyêt tối ưu một số bài toán” , sáng kiến này sẽ giúp học sinhcủa trường THPT Cầm Bá Thước vận dụng các kiến thức vào làm tốt các dạng đềthi học sinh giỏi tỉnh có liên quan đến phương pháp chia để trị, phục vụ cho việcbồi dưỡng học sinh giỏi của trường
II Mục đích của đề tài
- Mục tiêu chính của đề tài là giúp học sinh nắm được thuật toán tìm kiếmnhị phân; Tăng khả năng tư duy thuật toán, phát huy tính sang tạo, Năng lực vậndụng các kiến thức đã học vào giải quyết các bài toán
- Giúp cho việc ôn thi học sinh giỏi đạt kết quả cao
- Tạo ra nguồn tài liệu tham khảo về phương pháp cũng như thuật toán nhằm
hỗ trợ cho học sinh, giáo viên dạy bồi dưỡng học sinh giỏi tin học
III Đối tượng và phạm vi nghiên cứu
1 Đối tượng nghiên cứu
- Thuật toán tìm kiếm nhị phân và vận dụng vào các bài toán tối ưu sử dụngngôn ngữ lập trình C++
- Học sinh giỏi tin khối 11, 12, giáo viên giảng dạy học sinh giỏi tin
2 Phạm vi nghiên cứu
Sử dụng Thuật toán tìm kiếm nhị phân để giải một số bài toán bồi dưỡng họcsinh giỏi tin học 11, 12
IV Phương pháp nghiên cứu
- Thu thập, phân tích các tài liệu và thông tin liên quan đến Thuật toán tìmkiếm nhị phân
- Lựa chọn một số bài toán để sử dụng Thuật toán tìm kiếm nhị phân
- Thiết kế các bài toán đã được lựa chọn trong chương trình tin học 11 để bồidưỡng học sinh giỏi
Trang 3PHẦN 2 NỘI DUNG SÁNG KIẾN KINH NGHIỆM
I Cơ sở lý luận
I.1 Bài toán tìm kiếm
Tìm kiếm là việc thường xảy ra trong cuộc sống, chẳng hạn tìm kiếm họcsinh trong trường học, tìm kiếm đồ trong siêu thị, tìm kiếm kiến thức trên mạngInternet,…Tổng quát, trong một phạm vi nào đó, một tập hợp các đối tượng nào
đó, cần tìm đối tượng cụ thể thỏa mãn yêu cầu nào đó Các bài toán tìm kiếmthường đưa về tìm kiếm phần tử trong mảng chỉ số
I.2 Thuật toán tìm kiếm nhị phân
Chia để trị là một tư tưởng rất phổ biến trong cuộc sống và được áp dụng rấthiệu quả trong Tin học Tư tưởng cơ bản của phương pháp chia để trị là Người taphân bài toán thành các bài toán con, các bài toán con lại tiếp tục được phân thànhcác bài toán con nhỏ hơn, cứ tiếp tục như thế cho đến khi ta nhận được bài toáncon đã có thuật giải hoặc có thể dễ dàng đưa ra thuật giải Sau đó kết hợp nghiệmcủa các bài toán con để nhận được nghiệm của bài toán con lớn hơn để cuối cùngnhận được nghiệm của bài toán cần giải Thông thường các bài toán con được phânchia là cùng dạng với bài toán ban đầu chỉ có cỡ của chúng là nhỏ hơn
Thuật toán tìm kiếm nhị phân dựa trên tư tưởng chia để trị Với những dãy
số đã được sắp xếp, ta thường tìm cách thu hẹp phạm vi tìm kiếm bằng cách phânđôi vùng tìm kiếm, xác định xem khóa tìm kiếm ở phần đầu hay phần cuối Sau đóthu hẹp rồi lại phân đôi Quá trình đó xảy ra cho đến khi không tồn tại vùng tìmkiếm hoặc tìm được số cần tìm
Thuật toán tìm kiếm nhị phân thường phát biểu dưới bài toán sau:
“ Cho mảng n phần tử đã được sắp tăng dần và một phần tử x Tìm xem x cótrong mảng hay không”
Yêu cầu: Thuật toán này chỉ có thể được dung khi dãy số được sắp xếp đơnđiệu theo thứ tự tăng hoặc giảmdần
I.3 Hiệu quả của tìm kiếm nhị phân trong các bài toán tìm kiếm
Trong ngôn ngữ lập trình C++, việc sử dụng hàm rất phổ biến và dung hàmrất dễ dàng mà không phải nhớ một đoạn lệnh dài mà lại có thể tối ưu việc sắp xếp
Do vậy ta thường dung hàm sort để tối ưu việc sắp xếp các phần tử rồi dung thuậttoán tìm kiếm nhị phân Cấu trúc câu lệnh sort như sau:
sort(a,a+n);// sắp xếp mảng a tăng dần từ phần tử 0 đến phần tử thứ n-1Hoặc sort(a+1, a+n+1);// sắp xếp mảng a tăng dần từ phần tử thứ 1 đến phần
tử thứ n
Hoặc sort(a,a+n,greater<int>());//sắp xếp mảng a giảm dần từ phần tử 0 đến n-1Hàm sort này có độ phức tạp O(nlogn), sử dụng thuật toán sắp xếp nhanhQuicksort kết hợp với Selection Sort, có tốc độ sắp xếp nhanh hơn các thuật toánsắp xếp nổi bọt hay sắp xếp chọn, sắp xếp chèn, hoàn toàn có thể sắp xếp đượcdưới n=100.000 phần tử thì số bước sắp xếp khoảng 1.700.000, máy tính hoàn toàn
có thể thực hiện dưới 1 giây
Trang 4Cũng có trường hợp mảng tìm kiếm là mảng thường đã sắp xếp sẵn theo giátrị nên ta dễ dàngtìm kiếm trên mảng đã được sắp xếp.
Tư tưởng của thuật toán: chọn phần tử ở vị trí giữa làm chốt, chia dãy thành
2 phần có kích thước nhỏ hơn Sau đó so sánh phần tử cần tìm x với chốt, nếu xlớn hơn chốt tìm ở nửa sau của dãy, nếu x nhỏ hơn chốt tìm ở nửa trước của dãy(áp dụng với dãy tăng), quá trình trên tiếp tục cho tới khi tìm được x hoặc dãy chiakhông còn phần tử nào
Thuật toán:
Cho dãy số: A{n} gồm n phần tử nguyên tăng dần, tìm phần tử có giá trịbằng x
Bước 1:Xét đoạn mảng a[L H] ban đầu L=1; H=n;
Bước 2: Phần tử chốt đầu tiên được chọn giữa dãy k=(L+H)/2;
Bước 3: so sánh x với a[k], nếu a[k]=x thì đó là nghiệm, đưa ra nghiệm vàkết thúc tìm kiếm
Bước 4: Nếu a[k]>x ta lấy nửa đầu, thực hiện tìm kiếm trên đoạn a[L;k-1]Bước 5: Nếu a[k]<x ta tìm kiếm x ở nửa sau, thực hiện tìm kiếm trên đoạna[k+1 H]
Bước 6: quay lại bước 2
Có 2 giải thuật đệ qui và khử đệ qui để thực hiện thuật toán
Cách 1: Giải thuật đệ qui
int tim(int L,intH,int x)
Cách 2: Giải thuật khử đệ qui
int tim(short L,short H)
Trang 5Ttốt = O(1) ( x nằm ở vị trí giữa mảng)
Txấu = O(logn)
II Vận dụng thuật toán giải quyết tối ưu một số bài toán
II.1 Một số dạng bài sử dụng thuật toán tìm kiếm nhị phân
Dạng 1: Cho mảng A tăng dần, tìm vị trí của phần tử có giá trị bằng x
int BS (int dau, int cuoi, int x)
{
while (dau<=cuoi)
{
int giua = (dau + cuoi)/2;
if (a[giua]==x) dau = giua + 1;
else cuoi = giua – 1;
}
Return -1;
}
Với dạng này, nếu tìm thấy x thì
Dau sẽ là vị trí của phần tử nhỏ nhất, đầu tiên lớn hơn x trong mảng
Cuoi sẽ là vị trí của phần tử lớn nhất, cuối cùng bé hơn x trong mảng
Khi đó, việc tìm kiếm sẽ tiến sát đến 2 phần tử kẹp giữa giá trị x trong dãy.Tính chất này khá thú vị nhưng khó ứng dụng vì nó chỉ đúng khi x được tìm thấy.Đây là dạng phổ biến nhấ ttrong các bài toán tìm kiếm nhị phân
Ví dụ: Cho dãy số (an), với n là số tự nhiên, ai là các số nguyên dương Tìmphần tử trong dãy an có giá trị bằng x
Dữ liệu: vào từ bàn phím
- Dòng đầu tiên ghi số nguyên dương n<=105
- Dòng thứ 2 ghi n số nguyên dương phân biệt ai <=1018
- Dòng 3 ghi số nguyên dương T <= 105
- T dòng tiếp theo ghi mỗi dòng ghi số nguyên dương x
Kết quả: ghi ra màn hình T dòng, mỗi dòng thứ I ghi Y nếu trong dãy (an) tồn tạiphần tử x, ghi N nếu không tồn tại
Trang 6Phân tích: Trước hết cần sắp xếp dãy tăng dần bằng hàm sort Sửa đổi hàm
tim(): nếu tồn tại x thì trả về vị trí khác 0, còn nếu không tồn tại x thì trả về 0 Do
đó ta chỉ cần xét giá trị hàm bằng 0 hay không thì trả về Y hoặc N
Trang 7cuoi = giua – 1;
}else dau = giua +1;
}return ans;
- Dòng đầu ghi số nguyên dương n<=106
- Dòng 2 ghi số nguyên dương T<=105
- T dòng tiếp theo mỗi dòng ghi số nguyên dương x
Kết quả ghi ra màn hình T dòng, dòng thứ I ghi phân tử ui tương ứng nhỏ nhấtlớn hơn hoặc bằng x
Trang 8Phân tích: Với điều kiện đã cho thì dãy đã có tính chất là một dãy tăng Ta
cần cải tiến hàm tim() bằng cách thay điều kiện a[giua]>=k thì lưu lại chỉ số và thuhẹp phạm vi tìm kiếm lấy đoạn sau, còn không thì lấy đoạn đầu
Trang 9if (a[giua]<=x)
{ans = giua;
dau = giua +1;
}else cuoi = giua -1;
- Dòng đầu ghi số nguyên dương n<=105
- Dòng 2 ghi n số nguyên dương phân biệt ai<=1018
- Dòng 3 ghi số nguyên dương T<=105
- T dòng kế tiếp ghi mỗi dòng ghi số nguyên dương x
Kết quả ghi ra màn hình T dòng, dòng thứ I ghi phần tử có giá trị lớn nhất
mà nhỏ hơn hoặc bằng x, nếu không tồn tại thì ghi ra -1
Phân tích: Đầu tiên dung hàm sort để sắp xếp mảng tăng dần Sau đó ta cải
tiến hàm tim(), tìm phần tử có giá trị lớn nhất mà nhỏ hơn k thì thay điều kiệna[giua]<=k
Trang 10Phân tích:
Ta tiến hành tìm kiếm nhị phân trên xâu kí tự để tìm ra vị trí số 0 cuối cùngnhư sau:
- Tìm phần tử giữa xâu đang xét
- So sánh kí tự ở vị trí giữa xâu với kí tự 0
- Nếu kí tự giữa xâu là kí tự 0 thì ta tìm ở nửa sau của xâu, nếu không phải
kí tự 0 (mà là 1) thì ta tìm ở nửa trước của xâu
Trang 11- Dòng đầu tiên gồm duy nhất một số nguyên dương � (� ≤ 105);
- Dòng thứ hai là một dãy số nguyên � tăng dần gồm � phần tử �1, �2, … , ��
(|�� | ≤ 109);
- Dòng thứ ba là một số nguyên dương� (� ≤ 105) − số lượng câu hỏi;
- � dòng tiếp theo, mỗi dòng là một số nguyên � (|�| ≤ 109)
Dữ liệu ra: Gồm � dòng, mỗi dòng là vị trí số nguyên � tương ứng trên
dãy � hoặc in ra 0 nếu � không nằm trên dãy
Phân tích: Nếu sử dụng thuật toán tìm kiếm tuần tự, độ phức tạp khi tìm
kiếm sẽ là O(n2) với 105 câu hỏi Q và mỗi câu hỏi ta sử dụng 105 phép tính so sánhthì máy tính phải thực hiện là1010, máy tính sẽ không cho ra kết quả trong thời gian
1 giây Sử dụng tính chất tăng dần để tìm kiếm nhị phân với độ phức tạp O(log2n),
ta có thể sử dụng thuật toán với nhiều lần tìm kiếm, ở đâycó Q câu hỏi tương ứngvới Q lượt tìm kiếm, mỗi lần ta tìm kiếm một số x thì độ phức tạp giảm còn lànO(log2n), tương đương105log2105 xấp xỉ 17.105 phép toán, máy tính có thể xử lýtrong 1 giây Ở đây thuật toán được cài đặt theo kiểu đệ quy
Chương trình:
#include <bits/stdc++.h>
using namespace std;
Trang 12- Dòng đầu tiên là một số nguyên dương � ( � ≤ 105);
- Dòng thứ hai là dãy số nguyên �� không giảm gồm � phần tử �1, �2, … ,��
(|��| ≤ 109);
-Dòng thứ ba là số nguyên dương � (� ≤ 105);
-� dòng tiếp theo, mỗi dòng là một số nguyên � (|�| ≤ 109)
Dữ liệu ra: Gồm � dòng, mỗi dòng là số lượng số nguyên � trên dãy � với
Trang 13Phân tích: Bài này mở rộng hơn so với bài 2 ở chỗ đếm số phần tử = x của
dãy Ta phải xác định đoạn các phần tử =x bằng cách cải tiến hàm find Ta lấy vị tríphần tử đầu tiên sát về bên trái bằng hàm floor Nếu a[i]<x thì lấy phần sau cònkhông thì lấy phần đầu và cả phần tử giữa Hàm find sẽ trả về vị trí đầu tiên =x.Sau đó ta chỉ cần dung thêm biến đếm để xác định độ dài đoạn =x nữa thôi
II.3 Một số bài toán nâng cao sử dụng thuật toán tìm kiếm nhị phân
Bài 1 Bài toán Ham ming
Dãy số nguyên dương tăng dần, trong đó ước nguyên tố của mỗi số không quá
5 được gọi là dãy Hamming
Như vậy, 10 = 2×5 sẽ là một số trong dãy Hamming, còn 26 = 2×13 khôngthuộc dãy Hamming
Trang 14Phần đầu của dãy Hamming là 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15,
Yêu cầu: Cho số nguyên x (1 ≤ x ≤ 109) Hãy xác định số thứ tự của x trong
dãy Hamming
Dữ liệu vào:Từ tệp văn bản HAMMING INP:
- Dòng đầu tiên chứa số nguyên t (1 ≤ t ≤ 105),
- Các dòng tiếp theo mỗi dòng chứa một số nguyên x
Kết quả ra: Ghi ra tệp văn bản HAMMING OUT: kết quả mỗi test đưa ra
trên một dòng dưới dạng số nguyên hoặc thong báo -1 nếu không tồn tại số đótrongdãy Hamming
Vídụ:
111267891011121314
126-1789-110-1-1
Phân tích: Bởi x<=109 là một số lớn, số phần tử <=105 nên ta cần tối ưuthuật toán Dãy Hamming là một dãy số có qui luật và có tính chất tăng dần Ta tìmcách xây dựng mảng hamming theo giá trị, sắp xếp mảng này tăng dần, sau đó sửdụng thuật toán tìm kiếm nhị phân trên mảng này
Trang 15return find(res + 1 , y , gt);
return -1;
}
void init()//xâydựngmảng hamming
{
hamming[1] = 1;
hamming[0] = 0;
long long dem = 1,i = 1, j = 1, k = 1;
unsigned long longa,b,c,temp;
for(long longi = 0; i<= 29 ; i++)
{
a = pow (2,i); for (long long j = 0 ; j <= 18 ; j++) {
b = pow (3,j); for (long long k = 0; k <= 12; k++) {
c = pow (5,k); temp = a*b*c; if (temp <= 1000000000) {
hamming[dem] = temp; dem++; }
}
}
}
l = dem - 1; sort(hamming + 1,hamming + dem);//sắpxếpmảnghamming } int main() { freopen ("hamming.inp", "r", stdin); freopen ("hamming.out", "w", stdout); long longi,n,k; cin>> n; init(); for (i = 0; i<n ;i++) {
cin>> k; cout<< find(1,l,k) << '\n'; }
Trang 16return 0;
}
Bài 2 Bài toán AMB
Tại vương quốc HT, có N thành phố và 1 con đường duy nhất xuất phát từ thành phố 1 lần lượt đi qua các thành phố 2, 3, 4,…, N HT muốn xây K bệnh viện ở K thành phố trong vương quốc mình Gọi D i = quãng đường ngắn nhất từ thành phố I đến bệnh viện gần nhất D = Max{D 1, , D 2 ,…, D N }
Bạn hãy viết chương trình tìm D min?
Phân tích: Tìm kiếm nhị phân đáp án trong đoạn (0, ∞), với mỗi giá trị tìm
kiếm R, ta kiểm tra tính “xây dựng được” của R như sau (thuật toán tham lam):
- Xét các thành phố từ 1 N, với mỗi thành phố I chưa được “phủ sóng” bởi
một bệnh viện nào đó, ta tiến hành đặt một bệnh viện mới tại thành phố xa nhất về
phía N còn “phủ sóng” được tới I (khoảng cách tới I không quá R) và đi “phủsóng” các thành phố k sau j mà có khoảng cách tới j không vượt quá R
- Kết thúc quá trình “phủ sóng”, nếu số bệnh viện phải xây dựng không vượtquá K, ta cập nhật kết quả
Trang 17bool kt(long long x)//kiểm tra quá trình “phủ sóng”{
Trang 18}
}
int main()
{
freopen ("amb.inp", "r", stdin);
freopen ("amb.out", "w", stdout);
Trang 19freopen ("capso.inp", "r", stdin);
freopen ("capso.out", "w", stdout);
III Hiệu quả của sáng kiến
Sau khi sáng kiến kinh nghiệm này được GV áp dụng cho đội tuyển HSGtại trường THPT Cầm Bá Thước đạt được kết quả như sau: 100% học sinh độituyển khi gặp các bài toán cùng dạng đã nhận thức được thực trạng không giảiquyết các test lớn của chương trình, đồng thời biết vận dụng thuật toán tìm kiếmnhị phân để giải quyết bài toán
Trang 20PHẦN 3 KẾT LUẬN
Qua các bài toán trên chúng ta thấy được phần nào hiệu quả của phươngpháp tìm kiếm nhị phân Tuy nhiên, với cách sử dụng kĩ thuật này vẫn chưa phải làthuật toán tối ưu nhất trong tìm kiếm, không phải bài toán nào ta cũng áp dụngđược thuật toán này mà phải tùy vào nội dung bài toán để có hướng áp dụng chophù hợp Việc này đòi hỏi học sinh phải được tiếp xúc với rất nhiều các bài toántương tự, khi đó kỹ năng áp dụng thuật toán vào các bài tập mới hoàn thiện
Trong đề tài của mình tôi đã cố gắng đưa ra được một số dạng bài tậpthường gặp sử dụng thuật toán tìm kiếm nhị phân, mong rằng sẽ giúp ích cho giáoviên và học sinh trong quá trình giảng dạy và học tập
Do thời gian có hạn nên nội dung đề tài chắc chắn còn nhiều thiếu sót, rấtmong nhận được phản hồi góp ý từ đồng nghiệp để có thể hoàn thiện hơn và ápdụng đề tài vào giảng dạy
XÁC NHẬN CỦA THỦ TRƯỞNG ĐƠN VỊ
Thường Xuân, ngày 26 tháng 5 năm
2022
Tôi xin cam đoan đây là SKKN củamình viết, không sao chép nội dung củangười khác