Ứng dụng thuật toán chặt nhị phân để giải một số bài toán tìm kiếm SỞ GIÁO DỤC VÀ ĐÀO TẠO THANH HÓA TRƯỜNG THPT NHƯ THANH SÁNG KIẾN KINH NGHIỆM ỨNG DỤNG THUẬT TOÁN CHẶT NHỊ PHÂN ĐỂ GIẢI MỘT SỐ BÀI TOÁ[.]
Trang 1SỞ GIÁO DỤC VÀ ĐÀO TẠO THANH HÓA
TRƯỜNG THPT NHƯ THANH
SÁNG KIẾN KINH NGHIỆM
ỨNG DỤNG THUẬT TOÁN CHẶT NHỊ PHÂN
ĐỂ GIẢI MỘT SỐ BÀI TOÁN TÌM KIẾM
Người thực hiện: Lê Thị Liễu Chức vụ: Giáo viên
SKKN thuộc lĩnh vực (môn): Tin học
THANH HOÁ NĂM 2018
Trang 2MỤC LỤC
1 MỞ ĐẦU 1
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 1
2 NỘI DUNG……… … 2
2.1 Cơ sở lý luận 2
2 Thực trạng vấn đề .2
2.3 Ứng dụng thuật toán “Chặt nhị phân” 3
2.4 Hiệu quả 31
3 Kết luận, Kiến nghị 32
Trang 31 MỞ ĐẦU 1.1 Lí do chọn đề tài
Các bài toán tìm kiếm chiếm phần lớn trong các bài tập của Tin học, Có nhiều phương pháp tìm kiếm để giải quyết bài toán đó trong đó tìm kiếm nhị phân là phương pháp tốt vì thời gian thực hiện nhanh
Thuật toán tìm kiếm nhị phân lại chỉ được trình bày ngắn gọn, sơ lược trong sách giáo khoa 10, 11 và không có ví dụ vận dụng dẫn đến học sinh khó có thể ứng dụng để làm các bài tập
Trên thực tế khi gặp dạng toán tìm kiếm học sinh có thể dễ dàng tìm ra cách giải nhưng chưa tìm ra được cách giải tối ưu để lấy được hết điểm Đó là những lí do để tôi chọn
đề tài “ỨNG DỤNG THUẬT TOÁN CHẶT NHỊ PHÂN ĐỂ GIẢI MỘT SỐ BÀI TOÁN TÌM KIẾM”
1.2 Mục đích nghiên cứu
Giúp học sinh vận dụng thuật toán chặt nhị phân để giải được một số bài toán
1.3 Đối tượng nghiên cứu
- Thuật toán chặt nhị phân
- Một số ví dụ vận dụng, đánh giá độ phức tạp của thuật toán
1.4 Phương pháp nghiên cứu
- Nghiên cứu sách, báo, tài liệu điện tử
- Áp dụng vào giảng dạy và bồi dưỡng HSG Tin học 11
Trang 42 NỘI DUNG 2.1 Cơ sở lý luận
Bài toán tìm kiếm dạng đơn giản SGK lớp 10:
Cho dãy A gồm N số nguyên khác nhau: a1, a2,…,aN và một số nguyên K Cần biết có hay không chỉ số i mà ai=K (1<=i<=N) Nếu có hãy cho biết chỉ số đó.
Ý tưởng thuật toán tìm kiếm chị phân: Sử dụng tính chất dãy A là dãy số tăng dần, ta tìm cách thu hẹp nhanh phạm vi tìm kiếm sau mỗi lần so sánh khóa với số hạng được chọn Để làm điều đó ta chọn số hạng Agiua ở giữa dãy để so sánh với K, trong đó Giua=[N+1] div 2
Khi đó chỉ xảy ra một trong ba trường hợp sau:
- Nếu Agiua=k thì giua là chỉ số cần tìm Việc tìm kiếm kết thúc
- Nếu Agiua>k thì việc tìm kiếm tiếp theo chỉ xét trên dãy A1, A2, …, Agiua-1 (Phạm vi tìm kiếm mới bằng khoảng một nửa phạm vi tìm kiếm trước đó)
- Nếu Agiua<k thì việc tìm kiếm tiếp theo chỉ xét trên dãy Agiua+1, Agiua+2,…, AN
Quá trình trên sẽ được lặp lại một số lần cho đến khi hoặc đã tìm thấy khóa K trong dãy hoặc phạm vi tìm kiếm bằng rỗng
Đánh giá độ phức tạp:
- Thuật toán tìm kiếm nhị phân có độ phức tạp: O(logN),
- Tìm kiếm tuần tự độ phức tạp: O(N)
Tuy nhiên chỉ áp dụng tìm kiếm nhị phân khi dãy đã sắp xếp nên chúng ta thường sử dụng thuật toán sắp xếp nhanh (Qsort) với độ phức tạp O(NlogN) để sắp xếp dãy trước đó
2.2 Thực trạng vấn đề.
Thực tế trong quá trình giảng dạy,khi gặp một bài toán tìm kiếm các em chỉ mới đưa ra được cách giải thông thường mà chưa tìm được phương pháp giải tối ưu
Trang 5Khi học xong thuật toán tìm kiếm nhị phân ở SGK lớp 10 các em hầu như không biết vận dụng vào các bài toán, nguồn tài liệu tham khảo lại ít nên học sinh không hình thành được tư duy giải bài toán
2.3 Ứng dụng Thuật toán chặt nhị phân
Bài 1: Cho dãy A có N số Tìm số lượng các số có giá trị nhỏ hơn số nguyên M
Dữ liệu vào: Tệp ‘slbehon.inp’
- Dòng đầu tiên chứa số nguyên N cho biết số lượng phần tử của mảng A
- Dòng thứ hai chứa N số nguyên cách nhau
- Dòng thứ ba chứa số lượng Test Q
- Mỗi dòng Q tiếp theo chứa số nguyên M
Dữ liệu ra: Tệp ‘slbehon.out’:
- Đối với mỗi truy vấn số lượng các số có giá trị nhỏ hơn M cho test đó
Giới hạn:
1 ≤ N ≤ 105
1 ≤ A [i] ≤ 109
1 ≤ Q ≤ 105
1 ≤ M ≤ 105
Slbehon.inp Slbehon.out 5
1 4 10 5 6 4
2 3 5 11
1 1 2 5
Trang 6Cách 1: Xây dựng 1 hàm để đếm số lượng phần tử trong mảng A có giá trị bé hơn
M, hàm này được viết thường là duyệt tuần tự từ A1 đến AN, so sánh Ai với M và tăng biến lưu giá trị đếm Chương trình được viết như sau:
function dem(m:longint):longint;
var i:longint;
begin
dem:=0;
for i:=1 to n do
if a[i]<m then inc(dem);
end;
for i:=1 to n do read(f,a[i]);
readln(f);
readln(f,q);
for i:=1 to q do
begin
readln(f,x);
writeln(f1,dem(x));
end;
end;
Với cách giải trên ta có: Hàm Dem có độ phức tạp O(N) và bài toán có Q test nên độ phức tạp là Q.O(N) Với yêu cầu 1 ≤ N ≤ 105; 1 ≤ Q ≤ 105 thì cách giải trên không thực hiện hết được các test lớn có Q, N tối đa
Cách 2: Ta thực hiện sắp xếp mảng đã cho bằng thuật toán Qsort và áp dụng kỷ thuật
“chặt nhị phân” để xác định chỉ số cuối cùng của phần tử nhỏ hơn M Đôh phức tạp: O(Nlogn)
program So_luong_be_hon;
const fi='slbehon.inp';
fo='slbehon.out';
nmax=100000;
Trang 7var a:array[1 nmax] of longint;
n,m,q:longint;
f,f1:text;
procedure qsort(l,h:integer);
var i,j,tam,k:longint;
begin
i:=l;
j:=h;
k:=a[(l+h) div 2];
repeat
while a[i]<k do inc(i);
while a[j]>k do dec(j);
if i<=j then
begin
tam:=a[i];
a[i]:=a[j];
a[j]:=tam;
inc(i);
dec(j);
end;
until i>j;
if i<h then qsort(i,h);
if j>l then qsort(l,j);
end;
function tknp(x:longint):longint;
var d,c,giua,res:longint;
begin
res:=-1;
d:=1;
c:=n;
while d<=c do
Trang 8begin
giua:=(d+c)div 2;
if a[giua]>=x then c:=giua-1
else
if a[giua]<x then
begin
res:=giua;
d:=giua+1;
end;
end;
if res=-1 then tknp:=0
else tknp:=res;
end;
procedure doc;
var i:integer;
x:longint;
begin
assign(f1,fo);
rewrite(f1);
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do read(f,a[i]);
qsort(1,n);
readln(f);
readln(f,q);
for i:=1 to q do
begin
readln(f,x);
writeln(f1,tknp(x));
end;
Trang 9begin
doc;
close(f1);
end
Bài 2 Cho 1 dãy N số khác nhau Hãy tìm xem có bao nhiêu cặp số có chênh lệch là k
đơn vị
Dữ liệu vào: Tệp ‘chenhlech.inp’:
- Dòng đầu tiên là N - số lượng dãy số và số nguyên K (N≤105, K≤109)
- Dòng tiếp theo chứa N số nguyên trong dãy (A[i] ≤ 109)
Dữ liệu ra: Tệp ‘chenhlech.out’
- Gồm một số duy nhất là số cặp có độ chênh lệch k
Capso.inp Capso.out Giải thích test
6 2
1 3 2 4 9 5
3 3 cặp số đó là; (1,3) (3,5) và (2,4)
Cách 1: Sử dụng 2 vòng For để duyệt và tìm ra 1 cặp số chênh lệch k đơn vị thì tăng biến đếm
begin
doc;
dem:=0;
for i:=1 to n-1 do
for j:=i+1 to n do
if abs(a[i]-a[j])=k then inc(dem);
assign(f,fo);
Trang 10rewrite(f);
writeln(f,dem);
close(f);
end
Chương trình trên có độ phức tạp là O(N2) Với giới hạn input của bài toán là (N≤105) thì chương trình trên không thực hiện được với test tối đa
Cách 2: Sử dụng kỷ thuật “chặt nhị phân” Đầu tiên ta sắp xếp dãy tăng dần bằng Qsort Tiếp theo ta duyệt dãy A Tại Ai, nếu hàm TKNP(A[i]-k)=true thì ta tăng biến đếm (tìm được 1 số có chênh lệch k so với A[i]) Độ phức tạp O(NlogN)
program capso;
const fi='capso.inp';
fo='capso.out';
nmax=100000;
var f:text;
n,k,dem:longint;
a:array[1 nmax] of longint;
procedure doc;
var i:longint;
begin
assign(f,fi);
reset(f);
readln(f,n,k);
Trang 11for i:=1 to n do read(f,a[i]);
end;
procedure sort(l,h:longint);
var
i,j,tam,x:longint;
begin
i:=l;
j:=h;
x:=a[(l+h) div 2];
repeat
while a[i]<x do inc(i);
while x<a[j] do dec(j);
if i<=j then
begin
tam:=a[i];
a[i]:=a[j];
a[j]:=tam;
inc(i);
dec(j);
end;
until i>j;
Trang 12if l<j then sort(l,j);
if i<h then sort(i,h);
end;
function tknp(i:longint):boolean;
var d,c,g:longint;
begin
d:=1;
c:=n;
while d<=c do
begin
g:=(d+c) div 2;
if (a[g]=i) then
begin
exit(true);
end;
if a[g]<i then d:=g+1
else c:=g-1;
end;
exit(false);
end;
Trang 13procedure xuli;
var i:longint;
begin
sort(1,n);
for i:=1 to n do
if (tknp(a[i]-k)) then inc(dem);
end;
begin
doc;
dem:=0;
xuli;
assign(f,fo);
rewrite(f);
write(f,dem);
close(f);
end
Bài 3 Chọn quân bài
Cho một tập N quân bài, mỗi quân chứa một số nguyên dương Bạn cần phải chọn ra
ba quân bài sao cho tổng các số trên 3 quân bài gần với số M nhất và không vượt quá M
Dữ liệu vào: Tệp ‘quanbai.inp’:
Dòng 1 chứa 2 số N và M (N <=100, M <= 500000)
Dòng 2 chứa N số nguyên dương, mỗi số không quá 100000
Dữ liệu ra: Tệp ‘quanbai.out’:
Trang 14In ra tổng 3 quân gần M nhất và không vượt quá M.
Input luôn đảm bảo tồn tại đáp số
Quanbai.inp Quanbai.out
5 21
5 6 7 8 9
21
Cách 1: Duyệt 3 vòng For và tìm max Độ phức tạp O(n3)
program quanbai;
const fi='quanbai.inp';
fo='quanbai.out';
var f:text;
n,m,max:longint;
a:array[1 100] of longint;
procedure doc;
var i:longint;
begin
assign(f,fi);
reset(f);
readln(f,n,m);
for i:=1 to n do read(f,a[i]);
end;
Trang 15procedure xuly;
var i,j,z:longint;
begin
for i:=1 to n-2 do
for j:=i+1 to n-1 do
for z:=j+1 to n do
if (a[i]+a[j]+a[z]>max) and (a[i]+a[j]+a[z]<=m) then max:=a[i]+a[j]+a[z];
end;
begin
doc;
max:=0;
xuly;
assign(f,fo);
rewrite(f);
writeln(f,max);
close(f);
end
Cách 2: Sử dụng thuật toán chặt nhị phân Gọi a[z] là số lớn nhất trong bộ 3 số a[i], a[j], a[z] Tìm kiếm nhị phân số a[z] sao cho tổng 3 số <=M Lưu ý cần sắp xếp mảng A trước khi tìm kiếm Độ phức tạp O(N2logN)
Trang 16program quanbai;
const fi='quanbai.inp';
fo='quanbai.out';
var f:text;
n,m,max:longint;
a:array[1 100] of longint;
procedure doc;
var i:longint;
begin
assign(f,fi);
reset(f);
readln(f,n,m);
for i:=1 to n do read(f,a[i]);
end;
function tknp(i,j:longint):longint;
var d,c,g,s:longint;
begin
tknp:=0;
d:=j+1;
c:=n;
while d<=c do
Trang 17begin
g:=(d+c) div 2;
s:=a[i]+a[j]+a[g];
if s>m then c:=g-1
else
if s=m then
begin
tknp:=a[g];
exit;
end
else
begin
tknp:=a[g];
d:=g+1;
end;
end;
end;
procedure qsort(l,h:integer);
var i,j,tam,k:longint;
begin
i:=l;
j:=h;
Trang 18k:=a[(l+h) div 2];
repeat
while a[i]<k do inc(i);
while a[j]>k do dec(j);
if i<=j then
begin
tam:=a[i];
a[i]:=a[j];
a[j]:=tam;
inc(i);
dec(j);
end;
until i>j;
if i<h then qsort(i,h);
if j>l then qsort(l,j);
end;
procedure xuly;
var i,j,z:longint;
begin
for i:=1 to n-2 do
for j:=i+1 to n-1 do
Trang 19begin
z:=tknp(i,j);
if z=0 then break
else
if (a[i]+a[j]+z>max) and (a[i]+a[j]+z<=m) then
max:=a[i]+a[j]+z;
end;
end;
begin
doc;
qsort(1,n);
max:=0;
xuly;
assign(f,fo);
rewrite(f);
writeln(f,max);
close(f);
end
Bài 4 Có hộp N kẹo và mỗi hộp chứa A[i] cái kẹo Số thứ tự của các viên kẹo được
đánh số từ 1,2,3, đến hết Cho biết thứ tự của viên kẹo, hãy chỉ ra viên kẹo đó thuộc hộp kẹo thứ mấy
Dữ liệu vào: Tệp ‘Thutukeo.inp’
- Dòng đầu tiên sẽ chứa N (Số hộp)
Trang 20- Dòng tiếp theo cho biết số kẹo trong hộp thứ i
- Dòng tiếp theo sẽ chứa Q (Số lần test)
- Q dòng tiếp theo mỗi dòng chứa 1 giá trị nguyên là thứ tự của viên kẹo
Kết quả: Tệp ‘Thutukeo.out’:
- Chứa Q dòng In ra hộp kẹo tương ứng với thứ tự viên kẹo, mỗi kết quả trên 1 dòng
‘Thutukeo.inp’ ‘Thutukeo.out’ Ghi chú
2
2 3
2
2
4
1 2
Hộp đầu tiên sẽ có các viên kẹo thứ tự: 1, 2 Hộp thứ hai sẽ có các viên kẹo chỉ số: 3, 4, 5
Ý tưởng: Xây dựng mảng S với ý nghĩa S[i] là tổng các phần tử từ A[1] đến A[i] Thực hiện tìm kiếm nhị phân trên mảng S
program chiso_keo;
const nmax=100000;
fi='thutukeo.inp';
fo='thutukeo.out';
var a:array[1 nmax] of longint;
n,q,x:longint;
s:array[0 nmax] of int64;
f,f1:text;
function tknp(x:longint):longint;
var d,c,k:longint;
begin
d:=1;
c:=n;
while d<=c do