Tải bản đầy đủ (.doc) (41 trang)

Ứ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

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 (174.7 KB, 41 trang )

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Á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


MỤ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



1. 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.

1


2. 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 A giua ở 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 A 1, 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 AgiuaQuá 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.

2



Khi 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
5

Slbehon.out
1

1 4 10 5 6

1


4

2

2

5

3
5
11

3


Cá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ừ A 1 đế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]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;
4


var

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]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 iif 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
5


begin
giua:=(d+c)div 2;
if a[giua]>=x then c:=giua-1
else
if a[giua]

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;
6


end;

begin
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
62

Capso.out
3

Giải thích test.
3 cặp số đó là; (1,3) (3,5) và (2,4)

132495
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);

7


rewrite(f);
writeln(f,dem);
close(f);
end.
Chương trình trên có độ phức tạp là O(N 2). Với giới hạn input của bài toán là (N≤10 5)
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);

8


for 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]while xif i<=j then
begin
tam:=a[i];
a[i]:=a[j];
a[j]:=tam;
inc(i);
dec(j);
end;
until i>j;

9



if lif iend;

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]else c:=g-1;
end;
exit(false);
end;

10


procedure 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’:
11


In 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


21

56789

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;

12


procedure 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)
program quanbai;

13


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
begin

14


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;
k:=a[(l+h) div 2];

15


repeat
while a[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 iif 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
begin

16


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).
- Dòng tiếp theo cho biết số kẹo trong hộp thứ i.

17


- 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
Giới hạn: 1≤N,Q≤ 105 ;

1≤ A[i] ≤ 106

‘Thutukeo.inp’ ‘Thutukeo.out’
2
1

Ghi chú
Hộp đầu tiên sẽ có các viên kẹo thứ tự: 1, 2

23


Hộp thứ hai sẽ có các viên kẹo chỉ số: 3, 4, 5

2

2
2
4
Ý 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
begin
k:=(d+c) div 2;

18



if xbegin
tknp:=1;
exit;
end;
if s[k]=x then
begin
tknp:=k;
exit;
end;
if (s[k]>x) and (s[k-1]begin
tknp:=k;
exit;
end
else
if s[k]else c:=k-1;
end;
end;
procedure doc_xuly;
var i:longint;
begin
assign(f1,fo);
rewrite(f1);
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do
begin

read(f,a[i]);
19


s[i]:=s[i-1]+a[i];
end;
readln(f);
readln(f,q);
for i:=1 to q do
begin
readln(f,x);
writeln(f1,tknp(x));
end;
close(f1);
end;
begin
s[0]:=0;
doc_xuly;
end.
Độ phức tạp: O(N+Q*log(N))
Bài 5. Cho 1 xâu S chỉ chứa các ký tự in thường ['a'-'z'] và 1 số nguyên K. Hãy tìm các xâu
con liên tiếp của S có trọng lượng bằng K.
Trọng lượng của 1 ký tự được tính như sau: ['a']=1; ['b']=2; ['c']=3,…['z']=26.
Trọng lượng của xâu S được tính là tổng trọng lượng các ký tự có trong xâu S.
Dữ liệu đầu vào: Tệp ‘timxaucon.inp’:
Dòng đầu tiên chứa số lượng các trường hợp cần kiểm tra T. Với mỗi test dữ liệu được
cho trên 2 dòng, dòng đầu là số nguyên K và dòng sau là xâu S.
Dữ liệu ra: Tệp ‘timxaucon.out’
In ra số lượng các xâu con có trọng lượng bằng K, mỗi số trên 1 dòng.
Giới hạn:1<=T<=20; 1<=K<=26*|S| ; 1<=|S|<=1000000


Timxaucon.inp
2

Timxaucon.out
2
20


5

1

abcdef
4
abcdef

Chương trình:
program

timxaucon;

const fi='timxaucon.inp';
fo='timxaucon.out';
nmax=20;
var

f,f1:text;
luus:array[1..nmax] of ansistring;
s1,cs:array[0..nmax] of int64;

i,j,t,d,n:longint;

procedure doc;
var i:longint;
s:ansistring;
begin
assign(f,fi);
reset(f);
readln(f,t);
for i:=1 to t do
begin

21


readln(f,cs[i]);
readln(f,luus[i]);
end;
end;
procedure taobang(s:ansistring);
var i,x:longint;
begin
s1[0]:=0;
for i:=1 to length(s) do
begin
x:=ord(s[i])-96;
s1[i]:=s1[i-1]+x;
end;
end;


function tknp(x:longint):boolean;
var d,c,k:longint;
begin
tknp:=false;
d:=1;
c:=n;
while d<=c do

22


begin
k:=(d+c) div 2;
if s1[k]=x then
begin
tknp:=true;
exit;
end;
if s1[k]>x then c:=k-1
else d:=k+1;
end;
end;
begin
doc;
assign(f1,fo);
rewrite(f1);
for i:=1 to t do
begin
taobang(luus[i]);
n:=length(luus[i]);

d:=0;
for j:=0 to length(luus[i]) do
if tknp(s1[j]+cs[i]) then inc(d);

23