Kiến nghị...20 SỞ GIÁO DỤC ĐÀO TẠO THANH HÓA TRƯỜNG THPT HOẰNG HÓA 2 SÁNG KIẾN KINH NGHIỆM TÊN ĐỀ TÀI CHUYÊN ĐỀ TÌM KIẾM NHỊ PHÂN VÀ ỨNG DỤNG CÁC HÀM LOWER_BOUND , UPPER_BOUND TRONG BỒI
Trang 1MỤC LỤC
Trang
1 MỞ ĐẦU
1.1 Lí do chọn đề tài 1
1.2 Mục đích nghiên cứu 1
1.3 Đối tượng nghiên cứu 1
1.4 Phương pháp nghiên cứu .2
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 2
2.1.1 Thuật toán tìm kiếm nhị phân 2
2.1.2 Khái niệm các hàm lower_bound(), upper_bound có sẵn trong C++ và những hệ quả của các hàm……… 5
2.1.3 Thuật toán chặt nhị phân theo kết quả ……… 6
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến kinh nghiệm …… 9
2.3 Các sáng kiến kinh nghiệm hoặc các giải pháp đã sử dụng để giải quyết vấn đề ……….10
2.3.1 . Hướng dẫn học sinh một số bài tập sử dụng thuật toán tìm kiếm Nhị phân và sử dụng hàm lower_bound(), upper_bound trong C++ ….10 2.3.2 Hướng dẫn học sinh một số bài tập sử dụng thuật toán chặt nhị phân theo kết quả……… 14
2.4 Hiệu quả của sáng kiến kinh nghiệm đối với hoạt động giáo dục, với bản thân, đồng nghiệp và nhà trường 19
3 KẾT LUẬN, KIẾN NGHỊ 3.1 Kết luận 20
3.2 Kiến nghị 20
SỞ GIÁO DỤC ĐÀO TẠO THANH HÓA
TRƯỜNG THPT HOẰNG HÓA 2
SÁNG KIẾN KINH NGHIỆM
TÊN ĐỀ TÀI CHUYÊN ĐỀ TÌM KIẾM NHỊ PHÂN VÀ ỨNG DỤNG CÁC HÀM LOWER_BOUND (), UPPER_BOUND () TRONG BỒI
DƯỠNG HỌC SINH GIỎI TIN HỌC 11
Người thực hiện : Trương Thị Quý Chức vụ: Giáo viên
SKKN thuộc lĩnh vực : Tin học
THANH HÓA, NĂM 2022
Trang 2MỤC LỤC
Trang
1 MỞ ĐẦU
1.1 Lí do chọn đề tài 1
1.2 Mục đích nghiên cứu 1
1.3 Đối tượng nghiên cứu 1
1.4 Phương pháp nghiên cứu .2
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 2
2.1.1 Thuật toán tìm kiếm nhị phân 2
2.1.2 Khái niệm các hàm lower_bound(), upper_bound có sẵn trong C++ và những hệ quả của các hàm……… 5
2.1.3 Thuật toán chặt nhị phân theo kết quả ……… 7
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến kinh nghiệm …… 9
2.3 Các sáng kiến kinh nghiệm hoặc các giải pháp đã sử dụng để giải quyết vấn đề……….10
2.3.1 . Hướng dẫn học sinh một số bài tập sử dụng thuật toán tìm kiếm nhị phân và sử dụng hàm lower_bound(), upper_bound trong C++ ………10
2.3.2 Hướng dẫn học sinh một số bài tập sử dụng thuật toán chặt nhị phân theo kết quả………14
2.4 Hiệu quả của sáng kiến kinh nghiệm đối với hoạt động giáo dục, với bản thân, đồng nghiệp và nhà trường………
20 3 KẾT LUẬN, KIẾN NGHỊ 3.1 Kết luận 20
Trang 3
3.2 Kiến nghị 21
Trang 4Trong quá trình giảng dạy nhiều năm, tôi nhận thấy một số bài toán lập trình liênquan công việc tìm kiếm hay như đưa ra kết quả bài toán có thể áp dụng đượcthuật toán trên, đồng thời rèn luyện kĩ năng lập trình và khả năng tư duy cho họcsinh Do đó, tôi mạnh dạn đề xuất sáng kiến kinh nghiệm :
“ Chuyên đề tìm kiếm nhị phân và ứng dụng các hàm lower_bound (), upper_bound () trong bồi dưỡng học sinh giỏi tin học 11”.
1.2 Mục đích nghiên cứu
- Nghiên cứu các bài toán có dữ liệu lớn mà khi chúng ta sử dụng các
phương pháp như tìm kiếm tuần tự sẽ không xét hết được tất cả các trường hợpcủa bài toán
- Ứng dụng ngôn ngữ lập trình C++ vào những bài toán cụ thể để giải quyết
vấn đề
- Giúp học sinh giải quyết bài tập một chương trình có cấu trúc để giải một
bài toán trên máy tính
1.3 Đối tượng nghiên cứu
- Thuật toán tìm kiếm nhị phân
- Sử dụng hàm lower_bound(), upper_bound() có sẵn trong C++ để giải
quyết bài toán
- Thuật toán chặt nhị phân theo kết quả
- Một số bài toán cụ thể trong thực tế có áp dụng thuật toán
Trang 5
- Tìm hiểu khả năng vận dụng của từng đối tượng học sinh mỗi lớp dạy vềlập trình các bài toán để có phương pháp giảng dạy phù hợp
1.4 Phương pháp nghiên cứu
- Tìm hiểu thuật toán tìm kiếm nhị phân
- Tìm hiểu thuật toán chặt nhị phân theo kết quả
- Tìm hiểu các hàm lower_bound(), upper_bound có sẵn trong C++.
- Chọn lọc một số bài tập điển hình liên quan tới tìm kiếm và chặt nhị phântheo kết quả để minh họa, hướng dẫn học sinh luyện tập và thực hành
- Thu thập thông tin kết quả học tập về khả năng lập trình một số bài toán ởmột số lớp giảng dạy để có sự so sánh, đối chiếu thực nghiệm kết quả thực hiệncủa đề tài
- Tham khảo một số tư liệu trên internet và tài liệu của đồng nghiệp
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.
2.1.1 Thuật toán tìm kiếm nhị phân:
- b3: Thực hiện công việc so sánh :
+ Nếu a[g]=X ta có kết quả ans= g : Đưa ra kết quả ans=g rồi kếtthúc bài toán
+ Nếu a[g]>X : X có thể thuộc đoạn bên trái của a[g] và thực hiện tìm
X, lúc này vị trí c thay đổi c=g-1
+ Nếu vị trí a[g]> X : X có thể nằm đoạn bên phải của a[g] và thựchiện tìm kiếm X, lúc này vị trí d thay đổi d=g+1
Công việc so sánh và gán các giá trị d, c, g sẽ được lặp đi lặp lại cho đến khi d>cthì dừng và đưa ra kết quả không tìm thấy X trong dãy hay in ra kết quả là -1
Trang 8
while (d <= c) { int g = (d + c)/2;
if (a[g] >= x) { kq = g;
for (int i=1; i<=n; i++) cin >> a[i];
sort(a+1, a+n+1); cout << tknp1(1,n,x); return 0;
}
2.1.1.3 Xét ví dụ 2 :
Cho dãy số nguyên a 1, a 2, a 3, , a n đã được sắp xếp không giảm và một số nguyên X Tìm vị trí k lớn nhất sao cho a[k] <X Nếu trong dãy không có k thì thông báo -1.
Ý tưởng :
Do k là vị trí lớn nhất mà a[k] < X
Nên vị trí k là kết quả kq = tknp1(1, n, x ) - 1.
Vd : cho dãy : 1, 3, 5, 6, 6, 6, 7, 9 X=6 nên vị trí k là 3 và a[k]=5<6 Mà hàm
tknp1(1, n, x) sẽ cho kq là 4 nên kq bài toán là tknp1(1, n, x) – 1 = 4-1=3
2.1.2 Khái niệm các hàm lower_bound(), upper_bound () có sẵn trong C++ và những hệ quả của các hàm :
Để thay thế cho thuật toán tìm kiếm nhị phân thì trong ngôn ngữ lập trình C++
có 2 hàm dùng sẵn là lower_bound(), upper_bound().
Trang 9
2.1.2.1 Hàm lower_bound(a+1,a+n+1,x) -a : cho ra vị trí đầu tiên trong mảng
a có giá trị > = x( từ vị trí 1 đến n), nếu không có x trong mảng a thì nó sẽ đưakết quả là (n+1)
Tương tự ta có thể sử dụng hàm này để đưa ra vị trí đầu tiên a[k]>= x từ vị trí i đến vị trí j : Hàm lower_bound(a+i, a+j+1, x) - a.
Áp dụng :
Cho dãy số nguyên a 1, a 2, a 3, , a n dãy đã được sắp xếp không giảm và một số nguyên X Tìm vị trí k nhỏ nhất sao cho a[k] >=X
- Ta dùng hàm : kq = lower_bound (a+1, a+n+1, x) - a
2.1.2.2 Hàm upper_bound(a+1,a+n+1,x)- a : cho ra vị trí đầu tiên trong mảng
a có giá trị > x ( từ vị trí 1 đến n), nếu không có x trong mảng a thì nó sẽ đưa kếtquả là (n+1)
Tương tự ta có thể sử dụng hàm này để đưa ra vị trí đầu tiên a[k]> x từ vị trí i đến vị trí j : Hàm upper_bound(a+i, a+j+1, x) - a.
Áp dụng :
Cho dãy số nguyên a 1, a 2, a 3, , a n dãy đã được sắp xếp không giảm và một số
nguyên X Tìm vị trí k nhỏ nhất sao cho a[k] >X
Ta dùng hàm : kq = upper_bound (a+1, a+n+1, x) – a
2.1.2.3 Hệ quả 1: Để đếm số lượng các phần tử trong dãy a 1, a 2, a 3, , a n có giátrị bằng x.Dãy đã được sắp xếp không giảm
U= lower_bound( a+1, a+n+1, y) - upper_bound( a+1, a+n+1, x) ;
U là giá trị cần tìm
2.1.2.5 Hệ quả 3: Để đếm số lượng các phần tử trong dãy a 1, a 2, a 3, , a n đã đượcsắp xếp không giảm và một số nguyên x Đếm xem có bao nhiêu phần tử có giátrị >x
V= n+1 - ( upper_bound(a+1,a+n+1,x) -a);
Trang 10
2.1.3 Thuật toán chặt nhị phân theo kết quả.
Ý tưởng thuật toán :
B1 : Đánh giá kết quả nằm ở đoạn nào ?
R = mid - 1; hoặc L=mid+1 ;
}else L = mid + 1; hoặc R= mid -1 ;
}
}
Hàm check(mid) là chương trình con kiểm tra xem với giá trị mid có thoã mãnyêu cầu đề ra không
Nếu kiểm tra hàm check(mid) đúng thì mid là ứng cử viên cho kết quả và gán
tạm thời kq = mid Tiếp tục đi tìm kết quả tối ưu hơn Nếu bài toán yêu cầu tìmkết quả nhỏ nhất thì R = mid -1 và ngược lại để tìm kết quả lớn nhất thì gán L =mid+1 ;
Trường hợp nếu hàm check(mid) nhận kết quả sai thì giá trị l nhận kết quả L =mid +1 nếu muốn tìm kết quả nhỏ nhất và ngược lại R = mid -1 nếu tìm kết quảlớn nhất
Xét ví dụ sau :
Bài 1 Dãy con Tên file: SUB.CPP
Cho một dãy số nguyên dương a 1 , a 2 , , a N (10 < N <= 10 5 ), a i <=10 9 với mọi i=1 N và một số nguyên dương S (S < 10 9 ).
Yêu cầu : Tìm độ dài nhỏ nhất của dãy con chứa các phần tử liên tiếp của dãy
mà có tổng các phần tử lớn hơn hoặc bằng S
Dữ liệu vào: Đọc từ file SUB.INP gồm 2 dòng, dòng 1 chứa N và S ở dòng đầu.
Dòng 2 chứa các phần tử của dãy
Dữ liệu ra: Kết quả ghi vào file SUB.OUT, chứa độ dài của dãy con tìm được.
Trang 11- mid= (L+r)/2 : ứng cử viên của kết quả.
- Xây dựng mảng tổng tiền tố của mảng A là mảng sum
- Kiểm tra xem hàm check (int mid) : dãy con độ dài mid có thoã mãn tổng >=skhông
- Nếu ( sum[ j]- sum[ j- mid ] )>=s thì đoạn này thoã mãn yêu cầu, gán tạm thờikq=mid
- Và đi tìm kết quả tốt hơn thì gán r= mid-1 ;
Bài này có thể làm theo thuật toán tìm kiếm nhị phân và sự dụng 2 hàm
lower_bound(), upper_bound(), chuyển bài toán về tìm cặp (i, j) thoã mãn
sum[j] – sum[i-1] > = s hay tìm trong mảng sum có giá trị thoã mãn > =sum[i]+s không ?
Code tham khảo :
Trang 12for (int x,i=1; i<=n; i++)
{ cin >> x; s[i] = s[i-1] + x;
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến kinh nghiệm
Trên cơ sở nhiều năm được phân công giảng dạy khối lớp 11, trườngTHPT Hoằng Hóa 2, tôi đã lưu lại kết quả học tập và sự tiến bộ của học sinh ởmỗi năm học ở một số lớp để có sự đối chiếu và rút kinh nghiệm
Bảng số liệu kết quả đạt được khi sử dụng kiến thức liên quan tới lập trìnhgiải các bài tập về tìm kiếm nhị phân và chặt nhị phân theo kết quả của học sinh lớp
11 năm học 2020 - 2021 khi chưa thực hiện đề tài:
STT Lớp Sĩ số Đạt yêu cầu Không đạt yêu cầu
Trang 13
* Ưu điểm:
+ Đa số học sinh đã hiểu khái niệm cơ bản về thuật toán tìm kiếm
+ Một số em học sinh đã biết cách sử dụng thuật toán tìm kiếm nhị phân đểgiải bài tập
2.3 Các sáng kiến kinh nghiệm hoặc các giải pháp đã sử dụng để giải quyết vấn đề:
2.3.1 Hướng dẫn học sinh một số bài tập sử dụng thuật toán tìm kiếm
Nhị phân và hàm lower_bound(), upper_bound () trong C++
Bài tập 1 : Cho dãy số nguyên a 1, a 2, a 3, , a n Hãy đếm xem trong dãy số đã cho
có bao nhiêu cặp số ai, aj với i<j thoã mãn ai +aj =0
2 chứa các phần tử của dãy có |ai |<= 2.109
Dữ liệu ra: Kết quả ghi vào file count.out, một số nguyên duy nhất là số lượng
- Khắc phục bằng cách ta sự dụng hàm lower_bound(), upper_bound().
- a[i] +a[j]=0 nên a[i]= - a[j] chuyển về bài toán tìm kiếm đơn giản với x= -a[i]
Code tham khảo :
#include <bits/stdc++.h>
#define N int(1e5)
using namespace std;
Trang 14Bài tập 2 : Bài 2 Kết bạn Tên file: friend.cpp
Theo quan niệm của người Á Đông cổ, mỗi cá nhân khi sinh ra đều ứng vớimột ngôi sao, được gọi là sao chiếu mệnh Các hoạt động của cá nhân đều bị chiphối bởi ngôi sao này, kể cả quá trình kết bạn – hẹn hò Theo thuyết Âm dương– Ngũ hành, hai người chỉ có thể tạo lập mối quan hệ bền vững khi các sao chiếumệnh của họ không có các thuộc tính tương khắc Qua hàng nghìn năm quan sát
và chiêm nghiệm, các chiêm tinh gia đã ghi nhận được n sao và hầu hết các tính
chất tương sinh – tương khắc giữa chúng Để có thể nhanh chóng đáp ứng nhu
cầu kiểm tra độ tương hợp của các sao, hiệp hội ABS (Association of Broker for
Single) tạo lập cơ sở dữ liệu ghi nhận tính chất của tất cả các sao đã khảo sát
được Trong cơ sở dữ liệu này, các sao được đánh số từ 1 tới n; sao thứ i có một giá trị s i thể hiện khả năng thích nghi của sao gọi là độ thích nghi Hai sao khácnhau có thể có cùng độ thích nghi Thông qua độ thích nghi của các sao, người
ta xác định khả năng tương hợp của chúng Khả năng tương hợp của 2 sao đượctính bằng tổng 2 độ thích nghi của chúng
sao và số nguyên B Hãy xác định số lượng các cặp sao (i, j) với i < j và s i + s j =
B
Trang 15
Dữ liệu: Vào từ file văn bản FRIEND.INP:
Dòng đầu tiên ghi 2 số nguyên n, B (2 ≤ n ≤ 105, |B| ≤ 109),
Mỗi dòng trong n dòng tiếp theo ghi một số nguyên là độ thích nghi của
một sao, độ thích nghi có trị tuyệt đối ≤ 109
Kết quả: Đưa ra file văn bản FRIEND.OUT một số nguyên – số lượng cặp
sao có độ tương hợp B tìm được.
Ví dụ: Trong 5 sao với độ thích nghi 3, 5, 6, 5, 3 có 4 cặp có khả năng tương
hợp bằng 8
FRIEND.IN P
FRIEND.OU T
+ Tìm vị trí đầu tiên của (b-s[i]) trong đoạn s[1] … s[i-1] là vt1
+ Tìm vị trí cuối cùng của (b-s[i]) trong đoạn s[1] … s[i-1] là vt2
int u=lower_bound(a+1,a+i,k)-a;
int v=upper_bound (a+1,a+i,k)-a-1;
ans+=v-u+1;
Trang 16
} cout << ans<< endl;
return 0;}
Bài tập 3: Đếm tam giác Tên file: TRIANGLE.CPP
Cho 3 dãy số dương A, B, C cùng có N phần tử Hãy đếm xem có bao nhiêu bộ
3 số A[i], B[j] và C[k] mà 3 số này là 3 cạnh của 1 tam giác
Dữ liệu vào: từ file TRIANGLE.INP với cấu trúc:
- Dòng đầu chứa số nguyên n (n <= 1000)
- Dòng thứ hai chứa các số A1, A2, , An
- Dòng thứ ba chứa các số B1, B2, , Bn
- Dòng thứ tư chứa các số C1, C2, , Cn
Các số ai, bi, ci đều không vượt quá 104 và được ghi cách nhau bởi dấu cách
Dữ liệu ra: file văn bản TRIANGLE.OUT gồm một số S duy nhất là số lượng
Trang 17int a[N+5],b[N+5],c[N+5];
int n, ans;
int main() { freopen("humberger.inp","r",stdin);
freopen("humberger.out","w",stdout);
cin >> n;
for (int i=1; i<=n; i++) cin >> a[i];
for (int i=1; i<=n; i++) cin >> b[i];
for (int i=1; i<=n; i++) cin >> c[i];
sort(c+1,c+n+1);
for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) {
Ở đầu ra của một dây chuyền sản xuất trong nhà máy ZXY có một máy xếp tựđộng Sau khi kết thúc việc gia công trên dây chuyền, các sản phẩm sẽ đượcxếp vào các hộp có cùng dung lượng M Sản phẩm rời khỏi dây chuyền đượcxếp vào hộp đang mở (khi bắt đầu ca làm việc có một hộp rỗng được mở sẵn)
Trang 18nếu như dung lượng của hộp còn đủ để chứa sản phẩm Trong trường hợp ngượclại, máy sẽ tự động đóng nắp hộp hiện tại, cho xuất xưởng rồi mở một hộp rỗngmới để xếp sản phẩm vào Trong một ca làm việc có n sản phẩm đánh số từ 1đến n theo đúng thứ tự mà chúng rời khỏi dây chuyền Sản phẩm thứ i có trọnglượng là ai, i = 1, 2, …, n Ban Giám đốc nhà máy qui định rằng sản phẩm xuấtxưởng của mỗi ca làm việc phải được xếp vào trong không quá k hộp
Yêu cầu: Hãy giúp người quản đốc của ca làm việc xác định giá trị M nhỏ nhấtsao cho số hộp mà máy tự động cần sử dụng để xếp dãy n sản phẩm xuất xưởngcủa ca không vượt quá số k cho trước
Dữ liệu: Vào từ file văn bản ZXY.INP:
• Dòng đầu tiên chứa hai số nguyên n và k, (1 <= k <= n <= 15000);
• Dòng thứ i trong n dòng tiếp theo chứa số nguyên dương ai (ai <= 30000), i
=1, 2, …, n
Các số trên một dòng cách nhau ít nhất một dấu cách
Kết quả: Ghi ra file ZXY.OUT một số nguyên duy nhất là dung lượng của hộp
Ví dụ:
Ý tưởng: sử dụng chặt nhị phân theo kết quả
- l = max(ai); r = a1 + a2 + … + an chặt nhị phân trên đoạn [l, r]
mid = ( l+ r)/2, với mỗi giá trị mid ta kiểm tra xem có số lượng hộp cần dùng có
< = k, nếu đúng thì mid thoã mãn và đi tìm tiếp kết quả tốt hơn theo yêu cầu
Code tham khảo
#include <bits/stdc++.h>
#define N 15000 using namespace std;
int n,k,ans=0,sum=0;