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

(SKKN 2022) vận dụng mô hình tìm kiếm nhị phân và sử dụng các hàm tìm kiếm trong thư viện của ngôn ngữ lập trình c++ giúp học sinh giải quyết tối ưu một số dạng toán tìm kiếm nhằm nâng cao hiệu quả bồi dưỡng học sinh giỏi

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 (173.64 KB, 22 trang )

1. MỞ ĐẦU
1.1. Lý do chọn đề tài.
Trong các chuyên đề ơn thi học sinh giỏi thì các chun đề mơ hình thiết
kế thuật tốn chia để trị cùng với mơ hình Quay lui, mơ hình Tham lam, Quy
hoạch động tạo thành bộ công cụ quan trọng hỗ trợ thiết kế các thuật toán giải
quyết một số lượng lớn các bài tập một cách hiệu quả. Trong đó, nổi bật vẫn
phải kể tới một mơ hình thiết kế dựa trên ngun lí của thuật tốn tìm kiếm nhị
phân.
Được đánh giá là một trong những thuật toán phổ biến nhất trong lĩnh vực
Tin học, mơ hình tìm kiếm nhị phân có hiệu quả đặc biệt về thời gian chạy cũng
như sự rõ ràng về cách thức thiết kế thuật tốn. Có thể nói, tìm kiếm nhị phân
khơng chỉ đơn thuần là thuật tốn tìm kiếm mà cịn là một mơ hình tư duy thiết
kế thuật tốn phổ biến. Mơ hình này dựa trên ngun lí hoạt động của thuật tốn
tìm kiếm nhị phân, đó là tìm kiếm một phần tử trên một dãy giá trị đã được sắp
xếp bằng cách chia nhỏ miền tìm kiếm, sau mỗi lượt khơng gian tìm kiếm được
thu gọn giảm bớt một nửa.
Quá trình dạy tại các lớp mũi nhọn và ôn thi HSG cấp Tỉnh, tơi đã vận dụng
mơ hình tìm kiếm nhị phân để giúp các em nhìn nhận một bài tốn từ nhiều góc
độ khác nhau. Qua đó có thể dễ dàng nhận ra việc áp dụng mơ hình này để giải
bài tốn nào đó. Vì vậy, tơi đã chọn đề tài “Vận dụng mơ hình tìm kiếm nhị
phân và sử dụng các hàm tìm kiếm trong thư viện của ngơn ngữ lập trình C+
+ giúp học sinh giải quyết tối ưu một số dạng tốn tìm kiếm nhằm nâng cao
hiệu quả bồi dưỡng học sinh giỏi” làm sáng kiến kinh nghiệm của mình trong
năm học 2021 – 2022 để trao đổi với đồng nghiệp. Đây là một phương pháp tôi
đã thực hiện rất hiệu quả tại ngôi trường THPT Triệu Sơn 3, đồng thời cũng hy
vọng cách làm này sẽ được hoàn thiện, bổ sung và nhân rộng trong các trường
THPT khác trong Tỉnh.
1.2. Mục đích nghiên cứu.
- Phát triển kĩ năng thiết kế thuật tốn theo mơ hình chia để trị, tức là giáo
viên cần quan tâm kĩ tới khả năng nhận dạng bài tốn, khả năng nhìn ra mơ hình
tìm nhị phân trong bài tốn.


- Vận dụng mơ hình tìm kiếm nhị phân để đưa ra được thuật toán phù hợp.
Bên cạnh đó, cần quan tâm tới yêu cầu học sinh đánh giá độ phức tạp thuật tốn.
- Tìm hiểu và vận dụng các hàm tìm kiếm trong thư viện của ngơn ngữ lập
trình C++ để giải quyết các bài tốn tìm kiếm.
1.3. Đối tượng nghiên cứu.
- Mơ hình tìm kiếm nhị phân.
- Các hàm tìm kiếm trong thư viện của ngơn ngữ lập trình C++
- Một số dạng bài tập thi HSG các cấp.
- Sự tư duy, ý thức học tập của học sinh ôn thi học sinh giỏi.
1


1.4. Phương pháp nghiên cứu.
- Điều tra khả năng tiếp cận mơn Tin học lập trình của giáo viên THPT.
- Điều tra khả năng tiếp cận môn Tin học lập trình của học sinh THPT.
- Tham khảo tài liệu, nghiên cứu các đề thi, các bài toán thiên về tư duy
tốn học cơ bản chuyển về bài tốn lập trình,...
- Phương pháp thực nghiệm trên đối tượng là học sinh THPT.
2. Nội dung sáng kiến kinh nghiệm.
2.1. Cơ sở lí luận của sáng kiến kinh nghiệm.
Trong bối cảnh toàn ngành GD-ĐT đang nỗ lực đổi mới phương pháp dạy
học theo hướng phát huy tính tích cực chủ động của học sinh trong hoạt động
học tập. Điều 24.2 của Luật giáo dục đã nêu rõ: “Phương pháp giáo dục phổ
thông phải phát huy tính tích cực, tự giác, chủ động, sáng tạo của học sinh, phù
hợp với đặc điểm của từng lớp học, môn học; bồi dưỡng phương pháp tự học,
rèn luyện kỹ năng vận dụng kiến thức vào thực tiễn, tác động đến tình cảm, đem
lại niềm vui, hứng thú học tập cho học sinh ”.
Như vậy, chúng ta có thể thấy định hướng đổi mới phương pháp dạy học
đã được khẳng định, khơng cịn là vấn đề tranh luận. Cốt lõi của việc đổi mới
phương pháp dạy học ở trường phổ thông là giúp học sinh hướng tới việc học

tập chủ động, chống lại thói quen học tập thụ động. Với một số nội dung trong
đề tài này, học sinh có thể tự học, tự rèn luyện thơng qua một số bài tập, dạng
bài tập cụ thể.
2.2. Thực trạng của vấn đề cần giải quyết
Qua thực tế giảng dạy ở trường THPT Triệu Sơn 3, tôi nhận thấy khi học
đến chương trình tin học lớp 11 đa số học sinh đều cho rằng đây là mơn học khó
nhất trong các mơn học, nhiều em cịn sợ mơn học này.
Mơ hình tìm kiếm nhị phân thường được dạy ngay sau giai đoạn học sinh học
xong phần kĩ thuật lập trình cơ bản. Thời điểm này, học sinh đã có thể sử dụng
ngơn ngữ lập trình cùng với các cơng cụ có sẵn trong thư viện để thể hiện thuật
tốn. Tuy nhiên, khả năng tự mình nhìn nhận, đánh giá và thiết kế được một
thuật tốn cho bài tốn mới cịn hạn chế.
Khi gặp các bài toán phải sử dụng kiểu dữ liệu lớn nhiều em lúng lúng.
Việc giải các bài toán với kiểu dữ liệu lớn thực sự cần thiết cho các em khi làm
các bài tốn lập trình trong chương trình Tin học phổ thơng nói riêng và việc giải
quyết các bài tốn thực tế nói chung.
Thực tế cho thấy, các đề thi HSG của tỉnh Thanh Hóa và các tỉnh đều có
các bài tập tìm kiếm với số lớn. Nếu học sinh không hiểu rõ và nhận biết tốt thì
thường khi giải các bài tập này hay gặp nhiều sai sót hoặc khơng xử lý hết test
theo u cầu.

2


2.3. Các giải pháp giải quyết vấn đề
2.3.1. Tổng quan về mơ hình tìm nhị phân
Dựa trên thuật tốn tìm kiếm nhị phân, mơ hình thiết kế thuật tốn dựa trên
dạng này thường có phát biểu tổng qt dạng: Tìm kiếm một giá trị thoả mãn
điều kiện nào đó trên một miền giá trị được sắp thứ tự.
Nhận dạng bài tốn: Tìm một giá trị trên miền giá trị đã được sắp xếp.

Định hướng một số công việc cụ thể khi phân tích bài tốn:
- Xác định giá trị cần tìm kiếm là gì?
- Xác định miền cần tìm: là miền có giá trị đã được sắp xếpđó là miền nào?
- Q trình thực hiện:
* Chia đơi miền tìm kiếm:
+ Xác định vị trí chốt (giữa)
+ Dựa trên mối quan hệ giữavị trí chốt và giá trị cần tìm,
tiến hành thu hẹp miền tìm kiếm (nửa trái, nửa phải)
+ Lặp lại quá trình tìm kiếm với miền giá trị mới.
Mặc dù thuật tốn tìm kiếm nhị phân có nhiều cách cài đặt, ngồi ra thư viện
STL C++ cũng có các hàm hỗ trợ nhưng việc tự mình cài đặt và hiểu được mơ
hình là rất quan trọng trong việc vận dụng thiết kế thuật tốn theo mơ hình tìm
kiếm nhị phân.
Dưới đây là một cách cài đặt phổ biến trên C++:
int solve(int a[], int x, int L, int R)
//tìm giá trị x trên dãy a[] từ chỉ số L tới chỉ số R
{
ans = -1;
while(L<=R)
{
int mid = (L+R)/2;
if(a[mid]==x)
{
ans = mid;
break;
}
if(a[mid]else
R = mid-1;
}

return ans;
}
3


2.3.2. Độ phức tạp thuật toán.
Người ta chứng minh được độ phức tạp tính tốn của thuật tốn tìm kiếm
nhị phân trong trường hợp tốt nhất là O(1), trong trường hợp xấu nhất là
O(logN). Mơ hình tìm kiếm nhị phân chỉ thực hiện được với dãy đã được sắp
xếp, nên nếu dãy chưa sắp xếp thì cần phải tính đến thời gian cho việc sắp xếp
lại dãy trước khi áp dụng tìm kiếm nhị phân.
2.3.3. Các dạng bài tập tìm nhị phân
Dưới đây là một số dạng phát biểu chung của những bài tốn có thể giải
quyết bằng mơ hình tìm nhị phân.
2.3.3.1. Dạng 1: Tìm kiếm nhị phân trên dãy (mảng) có sẵn.
Ở dạng này ta cần tìm phần tử có giá trị bằng x trên dãy a[] đã sắp xếp.
Xét các ví dụ sau:
Ví dụ 1: (BINSEARCH.CPP)
(Nguồn: />Cho dãy số A gồm N số nguyên đã được sắp xếp tăng dần và Q truy vấn,
mỗi truy vấn là một số nguyên X. Với mỗi truy vấn, hãy tìm vị trí xuất hiện của
X trong A? Nếu khơng tồn tại giá trị X trong A, in ra -1.
Input:
- Dòng đầu ghi N, Q
- Dòng thứ hai ghi N số nguyên
- Q dòng tiếp theo mỗi dòng ghi một số nguyên X
Output:
- Với mỗi truy vấn, hãy in kết quả trên một dịng
Ví dụ:
Input
Output

53
2
12345
-1
2
5
8
5
Ý tưởng giải thuật 1: Tìm kiếm tuần tự. Độ phức tạp là O(n)
Ý tưởng giải thuật 2: Tìm vị trí phần tử x trên dãy A đã sắp xếp tăng dần. Nếu
không tồn tại x trong A, in ra -1.
+ Tìm kiếm nhị phân: Vì dãy này đã được sắp xếp theo tiêu chí tăng dần, nên ta
cứ chia đơi ra tìm x. Đầu tiên ta tìm vị trí ở giữa, so sánh x với giá trị ở vị trí
giữa, nếu x bằng thì coi như đã tìm thấy. Nếu x lớn hơn giá trị giữa thì ta phải
4


tìm phía bên phải của vị trí giữa, ngược lại thì tìm phía bên trái của vị trí giữa.
Độ phức tạp là O(log N).
Cài đặt hàm tìm kiếm trên C++
int bsearch(int a[], int l, int r, int x)
{
int ans = -1;
while(l <= r)
{
int mid = (l+r)/2;
if(a[mid]==x)
{
ans = mid;
break;

}
else if(a[mid] < x) l = mid + 1;
else r = mid - 1;
}
return ans;
}
Ví dụ 2: (LOWERBOUND.CPP)
(Nguồn: )
Cho dãy A được sắp xếp tăng dần . Có Q truy vấn, mỗi truy vấn là một số
nguyên k: Với mỗi k, hãy in ra số đầu tiên bé nhất có giá trị lớn hơn hoặc bằng k
gọi là P
Ví dụ: Dãy A = {1,2,2,3,4,4,4,5,6,6}
Với k = 2  số đầu tiên nhỏ nhất có giá trị lớn hơn hoặc bằng tại vị trí id=2;
Dãy số: A={1,2,2,3,4,4,4,5,6,6}
Input:
- Dịng đầu ghi N, Q
- Dịng thứ hai ghi N số nguyên
- Q dòng tiếp theo mỗi dòng ghi một số nguyên x.
Output:
- Với mỗi truy vấn, hãy in kết quả trên một dòng là số P; nếu không tồn tại
giá trị lớn hơn hoặc bằng k, in ra -1
Ví dụ:
Input
10 2
1223444566

Output
2
5
5



2
4
Ý tưởng giải thuật 1: Tìm kiếm tuần tự. Độ phức tạp là O(n)
Ý tưởng giải thuật 2 cải tiến : Tìm x đầu tiên nhỏ nhất có giá trị >=x
- ans = -1
- Khi L<=R
o giữa = (L+R)/2;
o Nếu a[giữa]o ngược lại tìm x trong [L, giữa-1], ans = giữa
- return ans
Cài đặt hàm tìm kiếm trên C++
int lower(int a[], int l, int r, int x)
{
//Tìm phần tử đầu tiên có giá trị >=x
int ans = -1;
while(l<=r)
{
int mid = (l+r)/2;
if(a[mid] >= x)
{
r = mid-1;
ans = mid;
}
else l = mid + 1;
}
return ans;
}
Ví dụ 3: Upper_bound

(Nguồn: />Cho dãy A được sắp xếp tăng dần . Có Q truy vấn, mỗi truy vấn là một số
nguyên k: Với mỗi k, hãy in ra số đầu tiên nhỏ nhất có giá trị > k gọi là P
Ví dụ: Dãy A = {1,2,2,3,4,4,4,5,6,6}
Với k = 2  số đầu tiên nhỏ nhất có giá trị > k=2 tại id=4 A =
{1,2,2,3,4,4,4,5,6,6}
Input:
- Dòng đầu ghi N, Q
- Dòng thứ hai ghi N số nguyên
- Q dòng tiếp theo mỗi dòng ghi một số nguyên x.
6


Output:
- Với mỗi truy vấn, hãy in kết quả trên một dịng là số P cần tìm
Ví dụ:
Input
10 2
1223444566
2
4
Ý tưởng giải thuật tìm nhị phân:

Output
4
8

- Giá trị cần tìm: số nhỏ nhất có giá trị >x
- Miền tìm kiếm A[1]…A[N] được sắp tăng dần
- Khi L<=R
o Chốt mid = (L+R)/2

o Nếu vị trí chốt > x:
 chốt cũng là một nghiệm tiềm năng ans = mid
 Thu hẹp miền tìm kiếm là nửa đầu dãy tức là [L, mid-1]
o Ngược lại:
 Tìm kiếm trên miền [mid+1, R]
Cài đặt hàm tìm kiếm trên C++
int upper(int a[], int l, int r, int x)
{
//Tìm phần tử có giá trị nhỏ nhất mà >x
int ans = -1;
while(l <= r)
{
int mid = (l+r)/2;
if(a[mid]>x)
{
ans = mid;
r = mid - 1;
}
else l = mid +1;
}
return ans;
}
Sử dụng một số hàm tìm kiếm có sẵn trong thư viện STL của C++
- Hàm binary_search: là hàm tìm kiếm nhị phân có độ phức tạp là O(logn)
7


Cấu trúc của hàm như sau:
binary_search(a+1, a+n+1, x) có nghĩa là tìm x trong dãy a (đã được sắp xếp
tăng dần) từ vị trí 1 đến vị trí n. Nếu có kết quả trả về true, ngược lại trả về false

binary_search(a, a+n, x): có nghĩa là tìm x trong dãy a (đã được sắp xếp tăng
dần) từ vị trí 0 đến vị trí n-1. Nếu có kết quả trả về true, ngược lại trả về false
- Hàm lower_bound(a+1, a+n+1, x) là cho vị trí đầu tiên của ơ nhớ có giá trị
lớn hơn bằng x của mảng a(a[1],a[2]…., a[n])
- Hàm upper_bound(a+1, a+n+1, x) là cho địa chỉ đầu tiên của ô nhớ có giá trị
lớn hơn x của mảng a(a[1], a[2],…a[n])
2.3.3.1.2. Một số bài tập vận dụng trong các đề thi học sinh giỏi
Bài tập 1: Count x ( Nguồn Đề thi HSG tỉnh Lâm đồng 2015)
Cho dãy A gồm N phần tử đã được sắp xếp tăng dần và Q truy vấn, mỗi
truy vấn gồm một số nguyên x yêu cầu đếm số lượng phần tử có giá trị là x?
Input:
- Dòng đầu ghi N, Q
- Dòng thứ hai ghi N số nguyên
- Q dòng cuối, mỗi dòng ghi một số nguyên x
Output: Với mỗi truy vấn, hãy in kết quả trên một dịng.
Ví dụ:
Input
Output
10 3
2
1223444566
3
2
0
4
9
Ý tưởng giải thuật: Với mỗi truy vấn:
- Tìm id1 là chỉ số vị trí x đầu tiên, id2 là chỉ số vị trí ngay sau x cuối cùng
 số lượng là id2-id1
- id1 = lower_bound; id2=upper_bound

Cài đặt hàm tìm kiếm trên C+
int countx(int a[], int l, int r, int x)
{
//Đếm trong dãy a từ a[l] đến a[r] số lượng phần tử x
int id1 = lower(a, l, r, x);
int id2 = upper(a, l, r, x);
return id2-id1;
}

8


Bài tập 2: COUNTONE.CPP (Nguồn: Đề thi HSG tỉnh Yên bái 2014)
Cho dãy A chỉ gồm các số nguyên 0, 1; Hãy đếm số lượng phần tử có giá
trị 1 trong dãy đã được sắp xếp giảm dần.
Input:
- Dòng đầu ghi số nguyên n là số lượng phần tử trong dãy (1 ≤ n ≤ 106)
- Dòng thứ hai ghi n số nguyên, mỗi số có giá trị là 0 hoặc 1.
- Dữ liệu đảm bảo dãy đã được sắp xếp giảm dần.
Output: Một số nguyên là số lượng phần tử 1 trong dãy
Ví dụ:
input
Output
5
3
111000
3
3
111
4

0
0000
Ý tưởng giải thuật1: (vận dụng các bài trên): id1 = lower(a,1,n,0);
id2=upper(a,1,n,0)  kq=id2-id1
Ý tưởng giải thuật 2 : tìm nhị phân trực tiếp trên dãy
- Nhận xét: dãy toàn số 0, rồi toàn số 1  Tìm vị trí i thoả mãn a[i]số lượng số 0 là n-i
- Tìm nhị phân như sau:
- L=1; R=n
- while(L<=R)
o mid=(L+R)/2
o Nếu a[mid]=1 && a[mid+1]=0 thì ans=mid
o Ngược lại nếu a[mid]=1 && a[mid+1]!=0 thì tìm ở [mid+1,R]
o Ngược lại nếu a[mid]=0 và a[mid+1]=0 thì tìm ở nửa đầu [L,mid-1]
Độ phức tạp của thuật toán là O(logn)
Cài đặt hàm tìm kiếm trên C++
int countone(int a[], int l, int r)
{
int ans = 0;
while(l<=r)
{
int mid = (l+r)/2;
if(a[mid]==1 && a[mid+1]==0){
ans = mid;
break;
9


}
else if(a[mid]==1 && a[mid+1]!=0)

{
l = mid+1;
}
Else
{
r = mid-1;
}
}
return ans;
}
Bài tập 3: Khieuvu3 - Khiêu vũ 3
(Nguồn: />Câu chuyện tình yêu Elo Cruz và Mara ở Philippines là một minh chứng
cho tình u đích thực, khơng màng đến ngoại hình. Họ khiến cư dân mạng thế
giới phải khâm phục vì một tình yêu bất chấp những khác biệt về ngoại hình.
Tuy nhiên admin rất lo lắng cho Elo, không biết anh chàng này sẽ phải chọn cây
ghế cao thế nào để hôn vợ. Cho nên trong buổi tiệc khiêu vũ “Cơn gió đêm hè!”
sắp đến đây admin muốn các cặp đơi có chiều cao chênh lệch phải đúng bằng K
mới được khiêu vũ cùng nhau.
Bạn hãy tính giúp cho admin xem có thể có bao nhiêu cách sắp xếp từng
cặp đơi với nhau thỏa mãn?
Input:
- Dòng đầu tiên là N - số lượng người tham gia bữa tiệc và số K (N≤10^5,
K≤10^9)
- Các dòng tiếp theo là chiều cao của N người tham gia bữa tiệc – khơng có
2 người nào có chiều cao giống nhau. (Hi ≤ 10^9)
Output:
- Gồm một số duy nhất là số cách lớn nhất có thể sắp xếp
Ví dụ:
Input
Output

62
3
132495
Ý tưởng giải thuật: Tìm kiếm nhị phân:
- Ta sắp xếp mảng chiều cao h thành dãy không giảm (tăng dần), vì nếu
(a,b) là một cặp thì cặp (b,a) khơng được tính nữa.
- Ta duyệt i từ 1 đến n-1, với mỗi i ta tìm: vị trí trái (left) là vị trí đầu tiên
trong đoạn từ [i+1, n] mà h[left]==h[i] +k. Vị trí phải (right) là vị trí cuối cùng
10


trong đoạn từ [i+1, n] mà h[right]== h[i] +k. Như vậy số cặp ghép với h[i] sẽ là
right – left +1.
- Độ phức tạp của thuật tốn là O(nlogn)
Ta có thể dùng hàm lower_bound và upper_bound có sẵn trong thư viện C++
Cài đặt hàm tìm kiếm bằng C++
void solve()
{
int left, right;
sort(h+1, h+n+1);
res = 0;
for (int i=1; i<=n-1; i++)
{
// tìm vị trí đầu tiên >= h[i] +k trong đoạn [i+1,n]
left= lower_bound(h+i+1, h+n+1, h[i]+k)- h
if (h[left!=h[i]+k) continue; // Nếu khơng có giá trị h[i]+k thì quay
lên vịng lặp thực hiện tiếp
//Tìm vị trí cuối cùng bằng h[i]+k
right = upper_bound (h+i+1, h+n+1, h[i]+k) – h -1
res=res + (right – left +1)

}
Bài tập 4. Hẹn gặp (Nguồn: Đề thi HSG cấp Tỉnh Thanh Hóa năm 2016-2017)
Thành phố Gloaming (Hồng hơn) nổi tiếng với đường dẫn vào công
viên thành phố. Các bức tượng tuyệt đẹp theo chủ đề thần thoại Hy lạp – La mã
đặt dọc theo con đường thẳng có một sức hút khơng cưỡng được với mọi khách
du lịch. Còn khi những tia nắng cuối cùng trong ngày miễn cưỡng rời khỏi bầu
trời thì sương mù dày đặc, như một tấm voan trắng mềm mại từ từ rũ xuống.
Bây giờ đứng cách quá r mét là đã khơng nhìn thấy mặt nhau và các bức tượng
trở thành nơi lý tưởng cho các đôi nam nữ thanh niên hẹn hò.
James Bond cần gặp gấp 2 điệp viên nội tuyến của mình để nhận các mật
báo khẩn. Khơng muốn 2 người này nhìn thấy nhau, Bond hẹn gặp mỗi người ở
một bức tượng sao cho khoảng cách giữa chúng lớn hơn r. Trên đường có n bức
tượng, bức tượng thứ i ở vị trí cách đầu con đường di mét i = 1 ÷ n, 1 ≤ d1< d2 < .
. .< dn ≤ 109.
Yêu cầu: Hãy xác định James Bond có bao nhiêu cách chọn địa điểm.
Dữ liệu vào: Vào từ file văn bản HENGAP.INP:
- Dòng đầu tiên chứa 2 số nguyên n và r (1 ≤ n ≤ 3×105, 1 ≤ r ≤ 109).
- Dòng thứ 2 chứa n số nguyên d1, d2, . . ., dn.
Kết quả: Đưa ra file văn bản HENGAP.OUT một số nguyên là số cách
chọn địa điểm tìm được.
Ví dụ:
11


HENGAP.INP
44
1358

HENGAP.OUT
2


Rằng buộc:
- Có ½ số test tương ứng với ½ số điểm có n ≤ 104
- Có ½ số test tương ứng với ½ số điểm có 104 < n ≤ 3×105
Ý tưởng giải thuật đề xuất:
- Dùng một vịng lặp For – do để duyệt i từ 1 đến n-1, trong đó với mỗi vị
trí i thì ta tìm kiếm nhị phân để tìm địa điểm j xa nhất thỏa mãn r+di < dj.
Cài đặt giải thuật trên C++ (Trình bày chi tiết ở phần phụ lục)
2.2.3.2. Dạng 2: Tìm kiếm trên miền kết quả (Binary search the answer)
Dạng tìm một phần tử trên dãy được sắp xếp là dạng cơ bản quen thuộc. Ta
có thể mở rộng việc tìm kiếm nhị phân trên miền kết quả. Bắt đầu bằng miền tìm
kiếm: là miền giá trị mà chắc chắn kết quả cần tìm sẽ thuộc vào đó. Với mỗi lần
tìm kiếm, chia đơi miền đó.
Giả sử ta có hàm check(x) trả về true nếu kết quả của x là có thể, ngược lại
trả về false. Với loại bài tốn dạng này, ta thường tìm giá trị lớn nhất, nhỏ nhất
của x sao cho check(x)=true.
- Nếu muốn tìm giá trị lớn nhất của x:
o Nếu check(x) là true thì check(y)=true với mọi
o Nếu check(x)=false, thì check(y)=false với mọi
- Nói cách khác, ta muốn giảm khơng gian tìm kiếm sử dụng hàm check ở
trên theo dạng sau: True – false
- Khi đó, cần tìm vị trí mà thay đổi từ True thành False bằng Tìm kiếm nhị
phân.
2.3.3.2.2. Một số bài tập vận dụng trong các đề thi học sinh giỏi
Bài tập 1: Nguồn: />Có N cây gỗ, có chiều cao lần lượt là A[1], A[2], .., A[n]. Bạn cần lấy một
lượng gỗ độ cao tối thiểu là M bằng cách chặt từ N cây theo cách như sau: chặt
tất cả những phần thừa của các cây có độ cao lớn hơn H. Hãy tìm giá trị H lớn
nhất để bạn có thể lấy được lượng gỗ tối thiểu là M.
Dữ liệu: Vào từ file văn bản PTIT126J.INP
+ Dòng 1 chứa 2 số nguyên N (1<=N<=106) và M (1 <= M <= 2.109).

+ Dòng 2 chứa N số nguyên A[1], A[2], …, A[n], là chiều cao mỗi cây gỗ
tương ứng (A[i] <= 109, i=1..N). Giả sử luôn tồn tại cách chặt.
Kết quả: Ghi ra file PTIT126J.OUT số H duy nhất.
Ví dụ:
PTIT126J.INP PTIT126J.OUT
47
15
12


20 15 10 17
5 20
36
4 42 40 26 46
Giải thích test thứ nhất:
Cây 1 chặt được (20-15)=5.
Cây 4 chặt được (17-15)=2.
Tổng số gỗ chặt được nếu H = 15 là 7.
Ý tưởng giải thuật đề xuất:
+ Bài này thực chất là bài đơn giản: Chặt nhị phân theo kết quả. Ta biết
rằng kết quả của bài toán nằm trong khoảng (0, max(a[i])). Với mỗi giá trị là H
thì ta kiểm tra xem tổng gỗ chặt ra có lớn hơn hoặc bằng M hay khơng. Nếu
đúng thì ta lại chặt nhị phân trên đoạn sau. Nếu sai thì ta lại chặt nhị phân trên
đoạn đầu.
Chương trình tham khảo viết trên ngơn ngữ C++:
#include <bits/stdc++.h>
using namespace std;
int i,n,h,nmax,j,dau,cuoi,t,k,m;
long long s;
int a[100001];

bool kt(long long h)
{
s=0;
for (j=1; j<=n; j++)
if (a[j]>h) s=s+a[j]-h;
if (s>=m) return(true);
return(false);
}
int main()
{
freopen("PTIT126J.inp", "r", stdin);
freopen("PTIT126J.out", "w", stdout);
cin>>n>>m;
for (i=1; i<= n; i++) cin>>a[i];
nmax=a[1];
for (i=1; i<=n; i++)
if (a[i]>nmax) nmax=a[i];
dau=0; cuoi=nmax;
while (dau<=cuoi)
{
h=(dau+cuoi) / 2;
13


if (kt(h)==true )
{
t=h;
dau=h+1;
}
else cuoi=h-1;

}
cout<< t;
return 0;
}
Bài tập 2: Nguồn: />Tại sân bay Nội Bài, một hành khách gồm M người chuẩn bị tham gia
chuyến bay. Vì số lượng khách quá lớn nên điểm kiểm soát của sân bay đã được
tăng lên thành N điểm. Tại điểm kiểm soát thứ i, cần mất T_i (s) để có thể kiểm
tra xong một người (tính cả thời gian đi bộ từ địa điểm xếp hàng tới điểm kiểm
tra này).
Các hành khách sắp xếp theo một hàng đợi. Lần lượt từng người vào một.
Hành khách ở đầu hàng đợi được phép đi vào một trạm kiểm sốt nào đó nếunhư
trạm kiểm sốt đó đang trống. Tuy nhiên, người đó cũng có quyền đứng chờ để
đợi một trạm kiểm soát khác trống để đi tới trạm đó, vì có thể giảm thiểu chi phí
thời gian cho cả đồn (xem ví dụ 1).
Các bạn hãy tính tốn xem thời gian nhỏ nhất có thể để đồn hành khách
kiểm tra xong hành lý là bao nhiêu?
Dữ liệu: Vào từ file P145SUMG.INP gồm:
+ Dòng đầu tiên gồm 2 số nguyên N và M, lần lượt là số quầy gửi đồ và
số vị khách (N <= 105, M <= 109).
+ N dòng tiếp theo, mỗi dòng là số nguyên T_i (1 <= T_i <= 109).
Kết quả: Ghi ra file P145SUMG.OUT đáp số của bài tốn.
Ví dụ:
P145SUMG.INP
P145SUMG.OUT
2
6 28
7
10
7
10 8

3
8
3
6
9
14


2
4
Giải thích test 1: Có 2 trạm kiểm sốt và 6 hành khách.
Tại t = 0, hành khách 1 và 2 bước vào trạm kiểm tra I và II. Tại t = 7, trạm
I trống, và người thứ 3 được phép đi. Tại t = 10, trạm II trống, người thứ 4 bước
vào kiểm tra. Tại t = 14, trạm I trống, người thứ 5 tới kiểm tra. Tại t = 20, trạm II
trống, nhưng hành khách thứ 6 sẽ đợi thêm một chút, tới t = 21, trạm I trống và
hành khách này sẽ tới kiểm tra, tổng chi phí thời gian bằng 28(s).
Thuật tốn:
+ Chặt nhị phân theo kết quả.
+ Kiểm tra xem với mỗi giá trị thời gian là X xem nó có thỏa mãn hay
khơng. Nếu thỏa mãn thì ta lại chặt nhị phân trên đoạn đầu, cịn nếu khơng thỏa
mãn thì ta lại chặt nhị phân trên đoạn sau. (cách kiểm tra giá trị X có thỏa mãn
hay khơng như code)
Chương trình tham khảo viết trên ngơn ngữ C++ (trình bày ở phụ lục )
Bài tập3: Trung bình lớn nhât
(Nguồn: Đề thi HSG lớp 12 Tỉnh Hưng Yên năm 2020 -2021)
Cho số nguyên ương n và dãy số A1, A2,….An. Gọi đoạn con [u,v] của dãy
là các phần tử liên tiếp Au, Au+1, Au+2,…., Av (u<= v). Dễ thấy đoạn con [u,v] có
độ dài là v-u+1 và giá trị trung bình là: Au+ Au+1 + Au+2+….+Av
v-u+1
Yêu cầu: Cho số nguyên k, hãy xác định đoạn con có độ dài khơng nhỏ

hơn k có giá trị trung bình lớn nhất.
Dữ liệu vào: file văn bản AVERAGE. INP
- Dòng đầu chứa 2 số nguyên dương n,k
- Dòng thứ 2 chứa n số nguyên A1, A2,….An (Ai <= 109, 1 <=i<=n), các số
được phân tách nhau bởi dấu cách
Dữ liệu ra: Đưa ra file văn bản AVERAGE.OUT một số thực duy nhất là
giá trị trung bình của đoạn con tìm được. Kết quả đưa ra lấy 3 chữa số thập phân
sau dấu phẩy
Ví dụ
AVERAGE. INP

AVERAGE.OUT

4 2

10.333

17 0 14 1
15


5 1

8.000

2 8 -1 4 5
Giải thích
- Trong ví dụ 1, đoạn con có giá trị trung bình lớn nhất thỏa mãn là đoạn [1, 3]
với các giá trị 17, 0, 14
- Trong ví dụ 2, đoạn con có giá trị trung bình lớn nhất thỏa mãn là đoạn [2, 2]

với giá trị là 8
Ý tưởng thuật toán đề xuất
Cách 1: Tính mảng tổng tiền tố: pref_sum[i]= a[1] + a[2] +….+ a[i] =
pref_sum[i-1] + a[i]
Xét cặp (i,j) với i<= j sao chp j-i +1 >=k
Kết quả là: res= max (res, (pref_sum[i]- pref_sum[i-1])/(j – i+1))
- Độ phức tạp O(n2)
Cách 2: Thực hiện phương pháp chặt nhị phân theo kết quả.
Ta biết cần tìm giá trị lớn nhất res thỏa mãn
>= res tương đương với (a[i]+… +a[j] ) >= res*(j-i+1)
Suy ra ta có: (a[i]- res) +…..+ (a[j] - res) >=0
Thực hiện chặt nhị phân để tìm kết quả res. Với mỗi giá trị mid, ta giảm mỗi
phần tử của dãy a[ ] đi mid đơn vị theo cơng thức trên, sau đó kiểm tra trong dãy
có đoạn con liên tiếp nào có tổng dương hay khơng? Nếu có thì ghi nhận giá trị
res = mid và tăng giá trị mid, ngược lại giảm giá trị mid.
- Độ phức tạp của thuật toán O(nlogn)
Cài đặt thuật tốn trên ngơn ngữ C++ (Được trình bày ở phụ lục )
Bài tập 4: Tìm giữa
(Nguồn: Đề thi HSG lớp 12 thành phố Hà Nội 2020-2021)
Cho hai số nguyên dương L và R.
Yêu cầu: Tìm số nguyên dương M (L<=Mnguyên liên tiếp từ L đến M và tổng các số nguyên liên tiếp từ M+1 đến R là
nhỏ nhất.
Dữ liệu: Vào từ tệp văn bản BÀI 1.INP
- Gồm hai số nguyên dương L và R (LKết quả: Ghi vào tệp văn bản BÀI 1.OUT
- Gồm một số nguyên duy nhất là số M thỏa mãn.
Ví dụ:
BÀI 1.INP
BÀI 1.OUT

Giải thích
2 7
5
Tổng từ 2 đến 5 là: 14
Tổng từ 6 đến 7 là 13
16


Chênh lệch là: 1
Ý tưởng thuật toán đề xuất
- Dựa vào miền kết quả theo đề bài, ta dùng phương pháp chặt nhị phân
- Đặt d=L, c=R
- Với giá trị mid = (L+R)/2
- Tính tổng từ d đến mid đặt là first_sum, tổng từ mid +1 đến c đặt là last_sum
- Cập nhật độ chêch lệch nhỏ nhất giữa first_sum và las_sum
- Nếu first_sum < las_sum thì điều chỉnh mid tăng lên tức là L=mid +1, ngược
lại điều chỉnh mid giảm xuống tức là R=mid-1
Cài đặt thuật tốn bằng ngơn ngữ lập trình C++(Được trình bày ở phụ lục)
Bài tập 5: Tam giác (Nguồn: Đề Thi HSG Tỉnh Thanh Hóa 2020-2021)
Hiền có N que tính, mỗi que có độ dài là một số nguyên dương và đôi một
khác nhau. Hiền muốn tạo ra các tam giác bằng cách ghép ba que tính với nhau,
độ dài mỗi cạch của tam giác là độ dài của một que tính.
Yêu cầu: Giúp Hiền tính xem tạo được bao nhiêu tam giác nhọn, tam giác
vuông và tam giác tù khác nhau từ các que tính này. Hai tam giác được gọi là
khác nhau nếu có ít nhất một que tính khác nhau
Dữ liệu vào: Đọc từ tệp CAU5.INP có cấu trúc như sau:
- Dịng đầu là số lượng que tính N (3<=N<=5000)
- Dịng thứ hai gồm N số nguyên dương không lớn hơn 10 4 là chiều dài của N
que tính.
Dữ liệu ra: Ghi ra tệp CAU5.OUT gồm 3 số nguyên lần lượt là số lượng tam

giác nhọn, tam giác vuông và tam giác tù tạo được
CAU5.INP
CAU5.OUT
6
2 1 4
2 12 9 10 3 15
Giới hạn:
- Có 25% test 3<=N<=300
- Có 75 % test cịn lại khơng có ràng buộc gì
Ý tưởng thuật tốn đề xuất
Để 3 số nguyên a,b,c tạo thành 3 cạnh của tam giác thì a,b,c phải thỏa mãn biểu
thức:
Điều kiện để 3 số nguyên a,b,c tạo thành 3 cạnh của:
- Tam giác nhọn: a2 + b2 > c2
- Tam giác vuông: a2 + b2 = c2
- Tam giác tù: a2 + b2 < c2
Cách 1:
17


- Duyệt 3 vịng for lồng nhau để tìm a,b,c tạo thành 3 cạnh của một tam giác,
nhọn, vuông, tù theo điều kiện đã nêu.
- Với cách này độ phức tạp của thuật tốn là O(n 3). Vì vậy, chỉ đạt 25% số điểm
khi N nhỏ.
Cách 2:
- Sắp xếp que tính tăng dần theo chiều dài của h[i]
- Dùng 2 vòng for lồng nhau để xác định cạnh a= h[i], b=h[j] (i- Để xác định số lượng tam giác, ta sẽ xác định số lượng giá trị c.
+ Sử dụng phương pháp chặt nhị phân trên đoạn từ h[j+1] đến h[n] để xác
định giá trị c như sau:

+ tìm vị trí p1 nhỏ nhất thỏa mãn
+ tìm vị trí p2 lớn nhất thỏa mãn h[p1] < a+b
- Để xác định số lượng giá trị c tạo thành tam giác nhọn, ta sử dụng tìm kiếm nhị
phân trên đoạn từ h[p1] đến h[p2] để tìm vị trí p lớn nhất sao cho (h[p]) 2 < a2 +b2
suy ra số lượng tam giác nhọn sẽ là c1=x-p1+1
Tương tự ta tìm được số lượng tam giác tù là c2
Đối với tam giác vng, tìm hai vị trí x1 nhỏ nhất và x2 lớn nhất sao cho
(h[x1])2 = (h[x2])2 = a2 + b2. Suy ra số lượng tam giác vuông là x2= x1 +1.
Với cách này độ phức tạp của thuật tốn là O(n2logn)
Cài đặt thuật tốn bằng ngơn ngữ lập trình C++ (trình bày tại phụ lục )
2.4. Hiệu quả của sáng kiến
Trong nhiều năm chịu trách nhiệm ôn thi HSG, tơi và nhóm Tin học của
trường THPT Triệu Sơn 3 đã áp dụng sáng kiến này. Tôi nhận thấy sau khi áp
dụng sáng kiến, các em học sinh trong đội tuyển học sinh giỏi cấp trường khơng
cịn cảm giác sợ những bài tốn tìm kiếm với số lớn nữa,các em hứng thú hơn và
hiểu rõ hơn nội dung, bản chất của thuật tốn tìm kiếm nhị phân. Đặc biệt, các
em đã nhìn nhận để giải quyết bài tốn bằng phương pháp này rất hiệu quả. Từ
đó, mở ra khả năng hứng thú học tập cho các nội dung (chuyên đề) khác, cách tư
duy bài toán cũng đa dạng và khoa học hơn. Các em mạnh dạn, tự tin để giải
quyết các bài toán trong các đề thi HSG cấp tỉnh của Thanh Hóa và các tỉnh
khác. Kết quả thi học sinh giỏi cũng tăng lên.
Dưới đây là một số kết quả trong kỳ thi HSG cấp tỉnh mà tôi và nhóm
chun mơn đã đạt được như sau:
Năm học 2014 -2015 tôi trực tiếp ôn luyện và đã đạt 1 giải khuyến khích
chiếm tỷ lệ 50% (thuộc em Lê Cơng Nhật Anh lớp 10B1). Các bài tập về tìm
kiếm đã được các em áp dụng và giải quyết tốt đạt điểm tối đa.
Năm học 2015 – 2016 tôi trực tiếp ôn luyện và đã đạt 1 giải ba và 1 giải
khuyến khích chiếm tỷ lệ 100% (thuộc 2 em Trịnh Việt Đức và Lê Mạnh Dũng
lớp 11B4). Các bài tập về tìm kiếm đã được các em làm rất tối ưu và đạt điểm
tối đa.

18


Năm học 2017 – 2018 tơi cùng nhóm tin ơn luyện và đạt 1 giải ba, 1 giải
khuyến khích chiếm tỷ lệ 100% (thuộc 2 em Lê Thị Hương và em Lê Văn
Cường lớp 11 A3). Kết quả này đưa thành tích bộ mơn Tin học xếp tốp 2 huyện
Triệu Sơn và Tốp 7 của Tỉnh Thanh Hóa.
Năm học 2018 – 2019 tôi trực tiếp giảng dạy, ôn luyện và đạt 1 giải nhì, 1
giải khuyến khích chiếm tỷ lệ 100% (thuộc 2 em Đào Huy Hiệu và em Hà Văn
Đức lớp 11E4). Kết quả này đưa thành tích bộ môn Tin học xếp tốp 1 huyện
Triệu Sơn và Tốp 5 của Tỉnh Thanh Hóa.
Năm học 2020-2021 tơi trực tiếp giảng dạy và ôn luyện đội tuyển học sinh
giỏi và đạt 1 giải ba và 1 giải khuyến khích chiếm tỷ lệ 100% (thuộc 2 em học
sinh Lê Ngọc Linh và Nguyễn Thị Châm lớp 12A35). Kết quả này đã đưa nhóm
Tin học xếp tốp 1 huyện Triệu Sơn Và Tốp 10 của Tỉnh Thanh Hóa
Năm học 2021 – 2022 tơi cùng nhóm tin ơn luyện và đạt 2 giải Nhì, 1 giải
Ba, 1 giải khuyến khích chiếm tỷ lệ 100% (thuộc 4 em Nguyễn Thành Quân,
Lương Xuân Hùng lớp 11A37, em Trần Đức Mạnh và em Hà Thọ Long lớp
12B36). Kết quả này đưa thành tích bộ mơn Tin học xếp tốp 1 huyện Triệu Sơn
và Tốp 4 của Tỉnh Thanh Hóa.
Tuy kết quả trên chưa phải là kết quả cao so với các trường THPT trên
địa bàn tỉnh nhưng đối với trường THPT Triệu Sơn 3 là trường xa trung tâm,
nguồn học sinh giỏi ít thì đây là kết quả khả quan và đáng được ghi nhận.
3. Kết Luận và kiến nghị
3.1. Kết Luận
Trường THPT Triệu Sơn 3 là một trường đóng xa trung tâm huyện, điều
kiện kinh tế của người dân đa phần rất khó khăn, trình độ nhân dân cịn thấp, coi
trọng các mơn chính nên việc thu hút và tìm nguồn bồi dưỡng học sinh giỏi mơn
Tin học rất khó khăn. Đa số học sinh có năng lực, khả năng bám giải cao đều lựa
chọn học đội tuyển Tốn, Lý, Hóa, Sinh… chưa thực sự quan tâm đến mơn Tin

học. Vì vậy việc tạo hứng thú trong ôn đội tuyển học sinh giỏi môn Tin học bằng
cách đưa ra các bài tập hay, lôi cuốn, rèn luyện tư duy cho học sinh là biện pháp
tốt để nâng cao chất lượng mũi nhọn cho nhà trường.
Như vậy có thể kết luận về hiệu quả mang lại sau khi triển khai các giải
pháp đã nêu là: Phần lớn các em học sinh đã hứng thú hơn trong học tập, chủ
động trong việc học tập, tự học, tự nghiên cứu, tìm hiểu kiến thức và rèn luyện
kỹ năng; nhiều em đã phát huy tối đa được tính sáng tạo và nhạy bén trong tư
duy, tự tìm tịi kiến thức, có sự say mê trong học tập và nghiên cứu. Kết quả học
tập của nhiều em học sinh có sự tiến bộ rõ rệt.
Đồng thời sau thời gian áp dụng các phương pháp trên tôi nhận thấy chất
lượng ôn đội tuyển môn Tin học ngày càng được nâng cao, trình độ chun mơn
của giáo viên đứng đội tuyển được củng cố vững chắc; giáo viên tự tin trong ôn
19


luyện học sinh. Vị thế của nhà trường so với các trường THPT trong Tỉnh cải
thiện rõ rệt.
3.2. Kiến nghị
Đối với giáo viên, phải không ngừng tự học, tự bồi dưỡng để hiểu biết về
công nghệ thông tin, biết khai thác thơng tin trên mạng Internet, có kĩ năng sử
dụng thành thạo các trang thiết bị dạy học hiện đại. Đặc biệt phải biết phát huy
các tính năng của trang thiết bị hiện đại trong việc thiết kế bài dạy.
Đối với các cấp lãnh đạo, cần quan tâm hơn về cơ sở vật chất như: Trang
thiết bị máy tính có nối mạng, máy chiếu Projector... tại các phòng học đa năng,
khuyến khích và động viên giáo viên áp dụng cơng nghệ thông tin vào dạy học.
XÁC NHẬN
CỦA THỦ TRƯỞNG ĐƠN VỊ

Thanh Hóa, ngày tháng năm 2022
Tơi xin cam đoan đây là SKKN của mình viết,

khơng sao chép nội dung của người khác.
Người viết

TRỊNH THỊ HẠNH

20


TÀI LIỆU THAM KHẢO
1. Tài liệu tập huấn ôn HSG của sở giáo dục đào tạo Thanh Hóa 2015 và 2017
(Tài liệu lưu hành nội bộ)
2. Cấu trúc dữ liệu và giải thuật của Lê Minh Hoàng
3. Trang giải bài trực tuyến SPOJ ()
4. Hệ Thống đề thi học sinh giỏi các tỉnh môn Tin học

21


DANH MỤC
CÁC ĐỀ TÀI SÁNG KIẾN KINH NGHIỆM ĐÃ ĐƯỢC HỘI ĐỒNG
ĐÁNH GIÁ XẾP LOẠI CẤP PHÒNG GD&ĐT, CẤP SỞ GD&ĐT VÀ CÁC
CẤP CAO HƠN XẾP LOẠI TỪ C TRỞ LÊN
Họ và tên tác giả: Trịnh Thị Hạnh
Chức vụ và đơn vị công tác: Giáo viên Tin học, trường THPT Triệu Sơn 3
Kết quả
Cấp đánh
đánh giá Năm
học
giá xếp loại
TT Tên đề tài SKKN

xếp loại đánh giá xếp
(Phòng, Sở,
(A, B, loại
Tỉnh...)
hoặc C)
1. Một số phương pháp tạo Sở GD&ĐT C
2010 - 2011
hứng thú cho học sinh khi
học lập trình Tin học 11
2. Một số phương pháp tạo Sở GD&ĐT B
2013 – 2014
hứng thú cho học sinh học
chương II -Tin học 12 qua
các phềm mềm minh họa trực
quan
3. Một số phương pháp tạo Sở GD&ĐT C
2016 – 2017
hứng thú cho học sinh khi
học Tin học 10
4. Một số phương pháp tạo Sở GD&ĐT C
2018 – 2019
hứng thú cho học sinh (Ban
khoa học tự nhiên) học ngơn
ngữ lập trình C++
5. Một số kinh nghiệm phân loại Sở GD&ĐT B
2019-2020
các dạng tốn điển hình trên
kiểu dữ liệu mảng nhằm nâng
cao chất lượng bồi dưỡng học
sinh giỏi môn Tin học

----------------------------------------------------

22



×