Một số học sinh gặp khó khăn trong việc nhận diện bài toán có thể áp dụng tìm kiếm nhị phân, từ đó hoặc không giải được bài hoặc áp dụng sai phương pháp, làm giảm hiệu quả giải quyết bài
Trang 1MỤC LỤC
1 Mở đầu 2
1.1 Lí do chọn đề tài 2
1.2 Mục đích nghiên cứu 2
1.3 Đối tượng nghiên cứu 2
1.4 Phương pháp nghiên cứu 3
2 Nội dung sáng kiến kinh nghiệm 3
2.1 Cơ sở lý luận 3
2.1.1 Tìm hiểu về tìm kiếm nhị phân………. 3
2.1.2 Các dấu hiệu nhận biết bài toán nhị phân………. 3
2.1.3 Cấu trúc mẫu khi viết thuật toán tìm kiếm nhị phân………… 4
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến……… 4
2.3 Các giải pháp giải quyết vấn đề 5
2.3.1 Dấu hiệu 1: Dữ liệu đã sắp xếp……… 5
2.3.2 Dấu hiệu 2: Bài toán có thể chuyển về dạng Đúng-Sai ……… 7
2.3.3 Dấu hiệu 3: Bài toán có ràng buộc lớn……… 13
2.4 Kết quả thu được 15
3.Kết luận, kiến nghị 15
3.1 Kết luận 15
3.2 Kiến nghị 15
4.Tài liệu tham khảo ……… 16
Trang 21 Mở đầu
1.1 Lý do chọn đề tài
Chương trình giáo dục phổ thông mới năm 2018 có rất nhiều sự thay đổi về nội dung cũng như phương pháp dạy học Nhưng sự thay đổi lớn nhất phải nói về môn
tin học Môn tin học là môn bắt buộc ở tiểu học và trung học cơ sở Đối với bậc trung học phổ thông là giai đoạn giáo dục định hướng nghề nghiệp nên ngay từ lớp 10 đã đưa vào chủ đề F “Giải quyết vấn đề với sự trợ giúp của máy tính” Đây là phần chủ đề đòi hỏi phải lập trình bài toán để máy tính giải quyết [1].
Trong chương trình Tin học bậc trung học phổ thông, đặc biệt là trong định hướng phát triển năng lực học sinh giỏi, các thuật toán cơ bản như tìm kiếm nhị phân (Binary Search) đóng vai trò quan trọng Đây là một thuật toán cơ bản nhưng hiệu quả cao, thường xuyên xuất hiện trong các đề thi học sinh giỏi cấp tỉnh, cấp quốc gia cũng như trong các kỳ thi tuyển chọn đội tuyển Olympic Tin học Tuy nhiên, qua quá trình giảng dạy và ôn luyện, tôi nhận thấy rằng nhiều học sinh dù đã được học thuật toán tìm kiếm nhị phân nhưng vẫn chưa thực sự hiểu rõ và vận dụng thành thạo Một số học sinh gặp khó khăn trong việc nhận diện bài toán có thể áp dụng tìm kiếm nhị phân, từ đó hoặc không giải được bài hoặc áp dụng sai phương pháp, làm giảm hiệu quả giải quyết bài toán Điều này cho thấy cần phải củng cố cho học sinh không chỉ về mặt kỹ thuật lập trình mà còn về tư duy nhận diện bài toán, khi nào thì bài toán có thể áp dụng tìm kiếm nhị phân Việc hình thành một hệ thống dấu hiệu nhận biết cơ bản, giúp học sinh dễ dàng phát hiện
“dạng bài” và chọn công cụ giải phù hợp là vô cùng cần thiết Đây là tiền đề để học sinh phát triển tư duy giải thuật và nâng cao hiệu suất giải quyết vấn đề trong kỳ
thi học sinh giỏi Chính vì vậy, tôi chọn đề tài: “Sử dụng một số dấu hiệu nhận biết cơ bản để củng cố cho học sinh một số bài tập về tìm kiếm nhị phân trong quá trình ôn thi học sinh giỏi Tin học THPT” với mong muốn chia sẻ kinh
nghiệm giảng dạy thực tiễn, giúp học sinh tiếp cận tư duy thuật toán theo hướng hiệu quả, chủ động và sáng tạo hơn
1.2 Mục đích nghiên cứu
- Hệ thống lại một số dấu hiệu nhận biết cơ bản để giúp học sinh xác định được
khi nào bài toán có thể áp dụng tìm kiếm nhị phân
- Hướng dẫn học sinh sử dụng dấu hiệu nhận biết cơ bản để khai thác bản chất
của thuật toán tìm kiếm nhị phân (Binary Search) trong các bài tập cơ bản
Trang 3- Góp phần nâng cao chất lượng ôn thi học sinh giỏi Tin học tại trường, tạo cơ sở
cho việc hình thành và phát triển tư duy thuật toán một cách khoa học và hiệu quả
1.3 Đối tượng nghiên cứu
- Học sinh lớp 11, 12: Đối tượng chính của nghiên cứu là học sinh trong các lớp THPT, đặc biệt là học sinh có nhu cầu ôn thi học sinh giỏi môn Tin học Các em học sinh này cần được trang bị kỹ năng giải quyết các bài toán về thuật toán, trong
đó có bài toán tìm kiếm nhị phân và các dấu hiệu nhận biết cơ bản khi giải quyết bài tập
- Bài tập ôn thi học sinh giỏi môn Tin học: Các bài tập tìm kiếm nhị phân trong
đề thi học sinh giỏi sẽ được sử dụng làm cơ sở để nghiên cứu, nhằm đánh giá khả
[1]: Chương trình tổng thể tin học 2018
năng áp dụng các phương pháp nhận biết cơ bản để giải quyết vấn đề một cách hiệu quả
-Thuật toán tìm kiếm nhị phân: Nghiên cứu sẽ tập trung vào việc cải thiện và phát triển các dấu hiệu nhận biết giúp học sinh dễ dàng nhận diện các bài toán tìm kiếm nhị phân trong đề thi và ứng dụng thuật toán này một cách chính xác và hiệu quả
1.4 Phương pháp nghiên cứu
- Phân tích đề thi học sinh giỏi tỉnh Thanh Hóa
- Thực nghiệm dạy học có hướng dẫn dấu hiệu nhận biết
- So sánh kết quả trước và sau khi áp dụng sáng kiến
2 Nội dung sáng kiến kinh nghiệm
2.1 Cơ sở lý luận
2.1.1 Tìm hiểu về tìm kiếm nhị phân [2]
Tìm kiếm nhị phân (binary search) là một thuật toán tìm kiếm trên dãy đã được
sắp xếp Ý tưởng chính của thuật toán là chia đôi dãy, so sánh phần tử giữa với giá trị cần tìm, từ đó loại bỏ một nửa dãy trong mỗi bước lặp, giúp giảm đáng kể số lần
so sánh so với tìm kiếm tuyến tính
Thuật toán tìm được thực hiện bằng cách liên tục thu hẹp phạm vi tìm kiếm Nếu giá trị của phần tử ở giữa bằng K thì thông báo tìm thấy Nếu giá trị của K nhỏ hơn giá trị của phần tử ở giữa thì thu hẹp phạm vi tìm kiếm là nữa đầu của dãy tăng A (ngược lại thì phạm vi tìm kiếm là nửa sau) Cứ tiếp tục thu hẹp phạm vi như vậy cho đến khi tìm thấy hoặc đã duyệt hết thì thông báo không tìm thấy Thuật toán tìm kiếm nhị phân được thực hiện như sau :
-Thiết lập các giá trị left, right là chỉ số phần tử đầu và cuối của dãy cần tìm Như vậy cần tìm K trong dãy A[left right] Ban đầu đặt left=0, right =n-1
- So sánh K với phần tử ở giữa dãy A[mid] , với mid là phần nguyên của phép chia (left+right) cho 2, có ba trường hợp có thể sảy ra:
+ Nếu K= A[mid] thì trả về chỉ số mid và kết thúc chương trình
+ Nếu K < A[mid] thì phần tử cần tìm sẽ nằm ở dãy con bên trái của phần tử A[mid], cập nhật lại giá trị right= mid -1, giữ nguyên giá trị left
+ Nếu K> A[mid] thì phần tử cần tìm sẽ nằm ở dãy con bên phải của phần tử A[mid] , cập nhật lại giá trị left=mid +1, giữ nguyên giá trị right
Lặp lại bước trên cho đến khi tìm thấy phần tử bằng K hoặc phạm vi tìm kiếm bằng rỗng (right <left)
Trang 42.1.2 Các dấu hiệu nhận biết bài toán nhị phân
Một trong những khó khăn phổ biến đối với học sinh khi giải bài toán là không nhận ra rằng bài toán có thể được giải bằng thuật toán tìm kiếm nhị phân Để khắc phục điều này, giáo viên cần trang bị cho học sinh một số dấu hiệu nhận biết cơ bản giúp xác định khả năng áp dụng tìm kiếm nhị phân:
Dưới đây là các dấu hiệu quan trọng:
a Dữ liệu đầu vào đã được sắp xếp:
- Đây là dấu hiệu kinh điển, dễ nhận thấy nhất Nếu mảng (hoặc danh sách) đầu vào đã được sắp xếp tăng hoặc giảm và đề bài yêu cầu tìm một giá trị cụ thể hoặc
[2]: Sách giáo khoa tin học 11
thỏa mãn điều kiện nào đó, thì tìm kiếm nhị phân là lựa chọn phù hợp
Ví dụ: "Tìm vị trí đầu tiên của số x trong mảng tăng dần A"
b Bài toán có thể chuyển về dạng ĐÚNG – SAI (YES/NO):
-Nếu bài toán có thể xây dựng một hàm kiểm tra check(x) sao cho:
check(x) = true với mọi x ≥ x0 (hoặc x ≤ x0)
check(x) = false ngược lại
Khi đó ta có thể tìm giá trị biên x0 bằng tìm kiếm nhị phân
c Bài toán có ràng buộc lớn, cần tối ưu hóa hiệu suất
Nếu bài toán có miền tìm kiếm lớn (ví dụ x từ 1 đến 109) và cần tìm một giá trị theo điều kiện, thì thường có thể áp dụng nhị phân nếu tồn tại tính đơn điệu.Ví dụ: "Tìm kích thước lớn nhất sao cho chia được ít hơn k đoạn"
2.1.3 Cấu trúc mẫu khi viết thuật toán tìm kiếm nhị phân: Trong sáng kiến
kinh nghiệm này tôi sử dụng ngôn ngữ lập trình c++ để minh họa ví dụ
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến
Trong quá trình giảng dạy và ôn luyện học sinh giỏi Tin học cấp trường, cấp tỉnh tại trường THPT, tôi nhận thấy học sinh có kiến thức cơ bản khá tốt, tư duy logic nhanh nhạy Tuy nhiên, khi tiếp cận với các bài toán yêu cầu tư duy thuật toán đặc biệt là dạng tìm kiếm nhị phân các em vẫn gặp không ít khó khăn Cụ thể:
a Khó khăn trong việc nhận diện bài toán nhị phân
Nhiều học sinh chỉ áp dụng được thuật toán tìm kiếm nhị phân trong trường hợp đơn giản như tìm một số trong mảng đã sắp xếp Khi gặp các bài toán cần nhị phân đáp án hay cần viết hàm kiểm tra đơn điệu phần lớn các em lúng túng không biết bắt đầu từ đâu
b Thiếu kỹ năng phân tích điều kiện áp dụng
Học sinh chưa biết cách kiểm tra tính đơn điệu của hàm kiểm tra, chưa phát hiện được biến độc lập tiềm ẩn trong bài toán để làm cơ sở áp dụng tìm kiếm nhị phân Các bài toán như “tối thiểu hóa” hay “tối đa hóa” một giá trị thỏa điều kiện thường
bị các em giải theo hướng vét cạn, hoặc không biết chọn hướng tiếp cận tối ưu
c Tư duy giải bài chưa linh hoạt
Nhiều em khi gặp bài toán lạ thường cố gắng biến đổi về các dạng đã học một cách máy móc mà không quan sát bản chất bài toán Điều này dẫn đến việc không phát hiện ra cấu trúc nhị phân có thể áp dụng hoặc giải sai vì áp dụng sai mô hình
Trang 5d Kết quả làm bài chưa tương xứng với năng lực
Thống kê kết quả làm bài các đề thi thử học sinh giỏi trong năm học trước cho thấy:
- Hơn 60% học sinh làm sai hoặc bỏ trống các bài tập dạng nhị phân nâng cao
- Chỉ khoảng 10–15% học sinh phát hiện được dấu hiệu cần thiết để áp dụng tìm kiếm nhị phân trong các bài toán có ẩn ý Hầu hết các em chỉ vận dụng được thuật toán ở mức cơ bản, thiếu chiến lược khai thác linh hoạt
e Nguyên nhân
- Chưa có hệ thống hướng dẫn cụ thể giúp học sinh nhận biết dấu hiệu đặc trưng của bài toán tìm kiếm nhị phân
-Việc giảng dạy thường chỉ dừng lại ở mức trình bày thuật toán, chưa đi sâu vào
kỹ năng phân tích và phát hiện khả năng áp dụng
-Thiếu hệ thống bài tập rèn luyện theo mức độ tư duy và biến thể của dạng bài
Từ thực trạng nêu trên, có thể thấy rằng việc xây dựng một hệ thống dấu hiệu nhận biết cùng với các bài tập hướng dẫn học sinh phát hiện và áp dụng tìm kiếm nhị phân một cách chủ động là hết sức cần thiết và cấp thiết trong dạy học Tin học
2.3 Các giải pháp giải quyết vấn đề:
2.3.1 Dấu hiệu 1: Dữ liệu đã sắp xếp
Bài 1: Cho dãy số A được sắp xếp theo thứ tự tăng dần gồm n (n<=105 ) số
nguyên và một số nguyên x Hãy tìm vị trí đầu tiên (chỉ số nhỏ nhất) mà A[i]= x Nếu không có thì trả về -1
Dữ liệu vào từ file BAI1.INP
-Dòng 1: Hai số nguyên n và x cách nhau 1 dấu cách
-Dòng 2 : Gồm n số nguyên của dãy số A
Kết quả ghi vào file BAI1.OUT: kết quả thõa mãn
7 3
1 3 3 3 5 6 8
1
5 3
1 2 2 2 2
-1
Cách 1: Duyệt tuyến tính (không tối ưu)
-Duyệt từ đầu đến cuối mảng, tìm phần tử đầu tiên bằng x
-Độ phức tạp: O(n)
-Không tối ưu với n = 105 hoặc n lớn hơn
Cách 2: Tìm kiếm nhị phân cải tiến
Vì mảng đã sắp xếp tăng dần, ta sử dụng tìm kiếm nhị phân (binary search) để tối ưu Mục tiêu: Tìm vị trí nhỏ nhất i sao cho A[i] = x, khi tìm thấy A[mid] = x, không dừng lại ngay, mà tiếp tục tìm về bên trái để kiểm tra xem có chỉ số nào nhỏ hơn cũng thỏa mãn không
Code chương trình tham khảo cách 2 :
#include <bits/stdc++.h> int main() {
Trang 6using namespace std;
int binarySearchFirst(int A[], int n, int x)
{
int left = 0, right = n - 1, res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (A[mid] == x) {
res = mid;
right = mid - 1;
} else if (A[mid] < x) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
freopen("BAI1.INP", "r", stdin); freopen("BAI1.OUT", "w", stdout); int n, x;
cin >> n >> x;
int A[100005];
for (int i = 0; i < n; ++i) cin >> A[i];
cout << binarySearchFirst(A, n, x) ; return 0;
}
Bài 2: Đề thi HSG tỉnh Thanh Hóa năm học 2016-2017
Thành phố Gloaming (Hoà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 du khách Có 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 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 hai người này nhìn thấy mặt nhau, Bond hẹn gặp mỗi người ở một bức tượng sao cho khoảng cách củ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 đường dᵢ mét (1 ≤ d₁ ≤ d₂ ≤ ≤ d ≤ 10 ).ₙ ⁹
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 file văn bản BAI3.INP:
Dòng đầu tiên chứa 2 số n và r (1 ≤ n ≤ 3×10⁵, 1 ≤ r ≤ 10 )⁹
Dòng thứ 2 chứa n số nguyên d₁, d₂, , d (đã sắp xếp không giảm)ₙ
Kết quả: Đưa ra file văn bản BAI3.OUT một số nguyên là số cách chọn địa điểm
tìm được
4 4
1 3 5 8
2
Cách 1:
-Duyệt tất cả các cặp (i, j) với i < j, kiểm tra d[j] - d[i] > r
-Độ phức tạp: O(n²) → không dùng được khi n = 3*10⁵
Trang 7Cách 2: Tìm kiếm nhị phân
-Với mỗi i, tìm vị trí nhỏ nhất j > i sao cho d[j] - d[i] > r
-Vì mảng d[] đã sắp xếp nên sử dụng tìm kiếm nhị phân
-Số cặp là n - j (các vị trí từ j đến n-1 đều thỏa mãn)
-Độ phức tạp: O(nlog n)
Code chương trình tham khảo :
[3]: Đề thi tin học tỉnh Thanh Hóa 2016-2017
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
long long d[MAXN];
int n;
long long r;
int main() {
freopen("BAI3.INP", "r", stdin);
freopen("BAI3.OUT", "w", stdout);
cin >> n >> r;
for (int i = 0; i < n; ++i) cin >> d[i];
long long count = 0;
for (int i = 0; i < n; ++i) {
int left = i + 1, right = n - 1, res = n;
while (left <= right) {
int mid = (left + right) / 2;
if (d[mid] > d[i] + r) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
count += n - res;
}
cout << count << endl;
return 0;
}
Trang 82.3.2 Dấu hiệu 2: Bài toán có thể chuyển về dạng Đúng-Sai (Yes/No) [4]: là
một dạng bài toán thường gặp trong các đề thi tin học hoặc trong thực tế, thuộc nhóm các bài toán tìm kiếm nhị phân kết hợp với kiểm tra điều kiện thường viết hàm để kiểm tra một điều kiều nào đó Các dạng bài toán dạng bài toán như sau :
Bài 1 : Một nhà máy có n sợi dây với độ dài lần lượt là a[1], a[2], , a[n] Người
ta muốn cắt các sợi dây này thành các đoạn có độ dài bằng nhau sao cho cắt được ít nhất k đoạn
Yêu cầu: Tìm độ dài lớn nhất của đoạn dây mà có thể cắt ra ít nhất k đoạn bằng
nhau từ n sợi dây đã cho
Dữ liệu vào đọc từ file BAI1.INP:
-Dòng 1: Hai số nguyên n và k (1 ≤ n ≤ 10⁵, 1 ≤ k ≤ 10 )⁹
-Dòng 2: n số nguyên a[i] là độ dài các sợi dây (1 ≤ a[i] ≤ 10 )⁹
Kết quả ra ghi ra file BAI1.OUT: Ghi một số nguyên là độ dài lớn nhất của đoạn
dây cắt ra được
3 7
10 15 20
5
Tìm độ dài lớn nhất L sao cho có
thể cắt các sợi dây ban đầu thành ít
nhất k đoạn dây có độ dài bằng
nhau bằng L
- Áp dụng nhị phân trên miền giá
trị của đáp án L trong đoạn [1,
max(a[i])]
-Mỗi bước nhị phân:
+Giả sử đang thử L = mid
+Tính tổng số đoạn cắt được với
L = mid
+Nếu ≥ k → lưu L là ứng viên tốt,
tiếp tục thử giá trị lớn hơn (left =
mid + 1)
+Nếu < k → L quá lớn, giảm
xuống (right = mid - 1)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int a[MAXN];
int n, k;
bool check(int len) { long long count = 0;
for (int i = 0; i < n; ++i) { count + = a[i] / len;
} return count >= k;
}
int main() { freopen("BAI1.INP", "r", stdin);
freopen("BAI1.OUT", "w", stdout);
cin >> n >> k;
int max_len = 0;
for (int i = 0; i < n; ++i) { cin >> a[i];
max_len = max(max_len, a[i]);
}
Trang 9int left = 1, right = max_len;
int res = 0;
while (left <= right) { int mid = (left + right) / 2;
if (check(mid)) { res = mid;
left = mid + 1;
} else { right = mid - 1;
} } cout << res << endl;
return 0;
}
Bài 2: Có n máy sản xuất, mỗi máy cần a[i] giây để sản xuất xong một sản
phẩm.Tất cả các máy đều hoạt động song song và không nghỉ Hỏi cần ít nhất bao nhiêu giây để tất cả các máy cùng sản xuất được tổng cộng ít nhất k sản phẩm
Dữ liệu vào từ file BAI2.INP:
-Dòng 1: Hai số nguyên n và k (1 ≤ n ≤ 10⁵, 1 ≤ k ≤ 10 )⁹
-Dòng 2: Gồm n số nguyên a[i] (1 ≤ a[i] ≤ 10 ), là thời gian để máy thứ i ⁹ làm ra một sản phẩm
Kết quả ra từ file BAI2.OUT: Một số nguyên duy nhất là thời gian nhỏ nhất
(tính bằng giây) để sản xuất đủ ít nhất k sản phẩm
3 7
3 2 5
8
-Tìm khoảng thời gian tìm kiếm:
T_min = 0, T_max = 1LL * k *
min(a[i])
(trong trường hợp tốt nhất, máy
nhanh nhất làm hết tất cả sản
phẩm)
-Với mỗi mid = (left + right)/2, tính
số sản phẩm tạo ra
-Nếu đủ ≥ k, thì thử giảm thời gian
(right = mid - 1)
-Nếu chưa đủ, phải tăng thời gian
(left = mid + 1)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int a[MAXN];
int n;
long long k;
bool check(long long t) { long long total = 0;
for (int i = 0; i < n; ++i) { total += t / a[i];
if (total >= k) return true;
} return false;
Trang 10} int main() { freopen("BAI2.INP", "r", stdin);
freopen("BAI2.OUT", "w", stdout);
cin >> n >> k;
int min_time = INT_MAX;
for (int i = 0; i < n; ++i) { cin >> a[i];
min_time = min(min_time, a[i]);
}
long long left = 0, right = 1LL * min_time * k;
long long res = right;
while (left <= right) { long long mid = (left + right) / 2;
if (check(mid)) { res = mid;
right = mid - 1;
} else { left = mid + 1;
} } cout << res << endl;
return 0;
}
Bài 3: Cho mảng a[1 n] gồm n số nguyên dương.
Yêu cầu: Chia mảng thành k đoạn con liên tiếp, mỗi đoạn là một dãy các phần tử
liên tiếp trong mảng.Mỗi đoạn có một tổng các phần tử Trong tất cả các cách chia, hãy tìm cách chia sao cho tổng lớn nhất trong các đoạn là nhỏ nhất có thể
Dữ liệu vào từ file BAI3.INP
-Dòng 1: Hai số nguyên n và k (1 ≤ k ≤ n ≤ 10⁵)
-Dòng 2: Gồm n số nguyên a[i] (1 ≤ a[i] ≤ 10⁴)
Dữ liệu ra file BAI3.OUT: Một số nguyên duy nhất là giá trị nhỏ nhất có thể của
tổng đoạn lớn nhất trong cách chia hợp lệ
5 3
1 2 3 4 5
6