1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Kĩ thuật sử dụng mảng đánh dấu trong lập trình qua một số ví dụ

20 71 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 839,5 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Để thu hút các em tham gia vào đội tuyển giáo viên phải luôn tìm những giải pháp giúp nâng cao chất lượng, hệ thống theo từng kỹ thuật các bài tập từ dễ đến khó, giúp các em nắm bắt được

Trang 1

1 MỞ ĐẦU 1.1 Lí do chọn đề tài

Môn tin học là môn học không thi THPT Quốc Gia, học sinh không chú trọng và đầu tư thời gian cho môn học này Số học sinh có hứng thú và yêu thích môn học cũng rất ít Để thu hút các em tham gia vào đội tuyển giáo viên phải luôn tìm những giải pháp giúp nâng cao chất lượng, hệ thống theo từng kỹ thuật các bài tập từ dễ đến khó, giúp các em nắm bắt được phương pháp áp dụng vào giải quyết các bài toán một cách tốt nhất

Những giải pháp và kỹ thuật đã có rất nhiều Tuy nhiên, trong quá trình đi sâu tìm hiểu các tài liệu, tham khảo đồng nghiệp, tôi phát hiện ra một kĩ thuật mà chưa thấy tài liệu nào viết đến, mà hầu như nó sử dụng khá nhiều trong các đề

thi học sinh giỏi Đó là sử dụng “Mảng đánh dấu” để lập trình Vì vậy tôi chọn vấn đề này để viết Sáng kiến kinh nghiệm “Kĩ thuật sử dụng mảng đánh dấu trong lập trình qua một số ví dụ”

1.2 Mục đích nghiên cứu :

Nhằm nâng cao chất lượng học sinh khá, giỏi có niềm đam mê tin học, giúp các em nắm vững kĩ thuật Từ đó hình thành tư duy logic, sự chắc chắn, chính xác để phục vụ cho giải quyết các bài toán lớn hơn và các công việc phức tạp sau này

1.3 Đối tượng nghiên cứu:

Đề tài này nhằm nghiên cứu về việc sử dụng kĩ thuật mảng đánh dấu để tối

ưu các bài toán đặc trưng, cơ bản trong tin học

1.4 Phương pháp nghiên cứu

+ Lấy các bài toán, thuật toán điển hình được áp dụng nhiều vào các bài toán khác

+ Tìm hiểu thực tế ở các đề thi học sinh giỏi qua từng năm

+ Tổng hợp lại các đề, xem xét dữ liệu bài toán yêu cầu, tìm ra các thuật toán có thể sử dụng được mảng đánh dấu

+ Thống kê, tập hợp lại các bài toán, xây dựng thuật toán, viết code

Trang 2

1.5 Những điểm mới của SKKN:

Để nâng cao chất lượng học sinh khá giỏi có niềm đam mê trong tin học, giúp các em nắm được vững kỹ năng trong lập trình, hình thành tư duy logic Tôi đã hệ thống các bài tập theo kỹ thuật và từ dễ đến khó, từ đó các em sẽ dễ dàng nắm bắt được kĩ thuật này để áp dụng giải quyết các bài toán phù hợp Nội dung sáng kiến kinh nghiệm tôi xin trình bày trong phần 2 dưới đây:

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

Khi được tiếp cận với cuốn “tài liệu chuyên tin học” của các tác giả Hồ Sĩ Đàm

(chủ biên), Đỗ Đức Đông, Lê Minh Hoàng, Nguyễn Thanh Hùng, Trong đó có

thuật toán sàng nguyên tố, hay còn gọi là thuật toán sàng nguyên tố Eratosthenes để tìm tất cả các số nguyên tố trong đoạn từ 1 tới N

Đó là

Bước 1: sử dụng một mảng để đánh dấu các phần từ từ 2-> N tất cả đều là nguyên tố, trong đó p = 2 là số nguyên tố đầu tiên

Bước 2: Tất cả các bội của p: 2p, 3p, 4p….sẽ bị đánh dấu vì không nguyên tố, Bước 3: Tìm các số còn lại trong danh sách chưa được đánh dấu > p Nếu không còn số nào , dừng lại tìm kiếm Nếu còn gán nó bằng x và lặp lại bước 2

Bước 4 Khi kết thúc giải thuật, các số không bị đánh dấu là số nguyên tố

Theo webside http://www.Wikipedia.org, Sau khi tìm ra giải thuật, nhà toán học Eratosthenes đã lấy lá cọ và ghi lại tất cả các số từ 2-100 Ông đã chọc thủng các hợp số và giữ nguyên các số nguyên tố

Tôi bắt đầu hình thành ý tưởng dùng kĩ thuật mảng đánh dấu để giải quyết các bài toán khác Và tôi đi tìm thêm các tài liệu, các đề thi học sinh giỏi qua các năm thì thấy sử dụng khá nhiều kĩ thuật này Và tôi bắt đầu tìm tòi, nghiên cứu

về kĩ thuật này

2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến kinh nghiệm.

Trước khi áp dụng sáng kiến kinh nghiệm về kĩ thuật “Mảng đánh dấu”,

tôi gặp rất nhiều bài toán cần xử lý số liệu lớn, áp dụng các thuật toán thông

Trang 3

thường chỉ giải quyết được một phần test có dữ liệu nhỏ, không full được test Khiến điểm thường thấp, không cao

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 đề

Sau đây là một số ví dụ cụ thể Tôi đã sử dụng giúp học sinh nắm vững được kỹ thuật dùng mảng đánh dấu trong trong lập trình

Trong quá trình nghiên cứu, tôi sử dụng ngôn ngữ lập trình Pascal để thể hiện các thuật toán Vì ngôn ngữ lập trình Pascal là ngôn ngữ phổ thông, đang được

sử dụng để giảng dạy trong chương trình hiện hành, để giúp các em học sinh, cũng như đồng nghiệp nắm được rõ hơn, sâu sắc hơn vấn đề tôi cần chuyển tải

Bài 1 Dãy dài nhất ( Kì thi chọn HSG cấp tỉnh lớp 12 THPT năm học

2017-2018 tỉnh Hải Dương)

Cho dãy số nguyên dương và số nguyên dương Hãy tìm dãy con dài nhất (là dãy có nhiều số nhất) gồm các số liên tiếp của A

mà tổng tất cả các số của dãy con này chia hết cho

Dữ liệu: Nhập từ file văn bản DAYDAINHAT.INP

 Dòng đầu tiên ghi hai số nguyên dương ghi cách nhau một dấu trống

 Dòng thứ hai ghi số nguyên dương mô tả dãy , hai số nguyên liên tiếp ghi cách nhau một dấu trống Giá trị các số nguyên không vượt quá 109

Kết quả: Ghi ra file văn bản DAYDAINHAT.OUT độ dài của dãy con dài nhất

tìm được

Ví dụ:

DAYDAINHAT.INP DAYDAINHAT.OUT

6 3

3 2 4 6 3 7

5

Ghi chú: Kết quả được chấm qua 6 test, mỗi test đúng được 0,25 điểm, trong đó:

 2 test có N≤500

Trang 4

 2 test có N≤5000

 2 test có N≤105

Ý tưởng: Lập một mảng tiền tố S với s[i] là tổng các phần tử từ từ phần từ đầu

tiên1 tới phần tử thứ i, như vậy tổng các đoạn a[i], a[i+1], ,a[j] được tính bởi s[j]-s[i-1] và ta có thuật toán với độ phức tạp O(n2) như chương trình minh họa dưới đây:

var n,k: longint;

a,s:array[0 100000] of longint;

i,j,T,res:longint;

begin

read(n,k);

for i:=1 to n do read(a[i]);

s[0]:=0;

for i:=1 to n do s[i]:=s[i-1]+a[i];

res := 0;

for i:=1 to n do

for j:=1 to n do

begin

t:=s[j]-s[i-1];

if (t mod k =0 ) and (res<j-i+1) then res:= j-i+1;

end;

write(res);

end.

Với thuật toán O(n2 ), ta giải quyết được với N<=5000 Tuy nhiên khi N<=105,

ta cần phải có một cách tiếp cận khác: (Lưu ý rẳng trong thuật toán trên ta chưa

sử dụng điều kiện K<=105 của đề bài) Ngoài ra lưu ý rẳng:

Tmod K = (S[j]-S[i-1]) mod K

Nên nếu T mod K = 0 thì S[i] và S[i-1] phải cùng đồng dư khi chưa cho K Do vậy, bài toán qui về là với mỗi j cần tìm vị trí đầu tiên đã có S[j] mod K Do K<=105 nên ta có thể sử dụng một mảng carry[0 100000] of longint để đánh

Trang 5

dấu (nhớ) xem số dư xuất hiện hay chưa? Ta có Chương trình minh họa dưới đây:

var n,kq,k:longint;

a,s,carry:array[0 100001] of longint;

i,j,du:longint;

BEGIN

readln(n,k);

for i:=1 to n do readln(a[i]);

s[1]:=a[1];

for i:=2 to n do s[i]:=s[i-1]+a[i];

for i:=1 to k do carry[i]:=-1;

carry[0]:=0;

kq:=0;

for i:=1 to n do

begin

du:=s[i] mod k ;

if (carry[du]<>-1) and (kq<i-du+1) then kq:=i-du+1;

if carry[du]=-1 then carry[du]:=i;

end;

write(kq);

readln

END.

Bài 2: Nghiên cứu gen (Đề thi HSG Bình Phước 2016-2017)

Trung tâm nghiên cứu gen thu thập N mẫu gen của N cụ Rùa trong cùng một thảo cầm viên N gen này được mã hóa thành N số nguyên dương A1, A2, ,

AN Bộ phận phân tích sau khi tính toán các cụ rùa có gen không đạt yêu cầu để loại ra không cho lai tạo nữa đã đưa ra kết luận sau:

Cụ rùa có gen không đạt yêu cầu là gen có số nguyên đại diện có tổng các ước nhỏ hơn nó mà nhỏ hơn nó.

Trang 6

Ví dụ: số 9 có các ước nhỏ hơn nó là 1, 3 ta có 1+3=4<9 Vậy gen được mã hóa thành số 9 là gen không đạt yêu cầu

Yêu cầu

Hãy đếm xem trong mẫu gen của N cụ rùa có bao nhiêu gen không đạt yêu cầu

Dữ liệu vào từ tệp văn bản GEN.INP

o Dòng đầu là số nguyên dương N.

o Dòng thứ hai là mã gen của Ncụ rùaA1, A2, , AN(các số cách nhau 1 dấu cách )

Dữ liệu ra ghi vào tệp văn bản GEN.OUT

o Một số nguyên duy nhất là số gen không đạt yêu cầu

Giới hạn

o

o

Ví dụ

5

3 6 9 12 7

3

Ràng buộc

o 70% test ứng với 70% số điểm của bài toán có:

o 15% test ứng với 15% số điểm của bài toán có :

o 15% test ứng với 15% số điểm của bài toán có :

Phân tích ý tưởng: Đối với bài này, Nhìn qua nhiều người sẽ nghĩ đến phương

án dùng công thức tính tổng ước Nhưng, cách đó không thể full test Ở đây ta

Trang 7

phải sử dụng kĩ thuật mảng đánh dấu bằng cách như sau: Thiết lập một mảng F đánh dấu lần lượt tổng ước của các số từ 1 đến 106 (các ước bé hơn chính nó) Ban đầu gán tất cả các phần tử trong mảng F = 1 (1 luôn là ước của tất cả các

số ) Xét các F[i*j] với i chạy từ 2 tới r= [ ] , j chạy từ i tới r div i F[i*j]

= F[i*j] +i , khi i<>j cộng thêm j vào nữa là ta đươc mảng F với F[i] là tổng các ước (bé hơn i) của i

r:=5000000;

for i:=1 to r do f[i]:=1;

for i:=2 to trunc(sqrt(r)) do

for j:=i to r div i do

begin

f[i*j]:=f[i*j]+i;

if i<>j then f[i*j]:=f[i*j]+j;

end;

Cuối cùng chỉ việc ngồi đếm, nếu a[i] > F[a[i]] thì tăng biến đếm lên

Độ phức tạp : nhỏ hơn nlogn

Code tham khảo:

const fi='gen.inp';

fo='gen.out';

var l,r,dem,n:longint;

f,A:array[-1 5000000] of longint;

procedure nhap;

var g:text;

i:longint;

begin

assign(g,fi);

reset(g);

readln(g,n);

for i:=1 to n do

Trang 8

read(g,A[i]);

close(g);

end;

procedure xuli;

var i,j,r:longint;

begin

r:=5000000;

for i:=1 to r do f[i]:=1;

for i:=2 to trunc(sqrt(r)) do

for j:=i to r div i do

begin

f[i*j]:=f[i*j]+i;

if i<>j then f[i*j]:=f[i*j]+j;

end;

dem:=0;

for i:=1 to n do

if A[i]>f[A[i]] then inc(dem);

end;

procedure xuat;

var g:text;

begin

assign(g,fo);

rewrite(g);

write(g,dem);

close(g);

end;

begin

nhap;

xuli;

xuat;

Trang 9

Bài 3: Tập số (Đề thi HSG tỉnh Thanh Hóa năm học 2016-2017)

Cho số tự nhiên N (1 ≤ N ≤ 106) và một tập A chỉ gồm các số tự nhiên

khác nhau được xác định như sau:

- 1 thuộc A.

- Nếu k thuộc A thì 2k+1 và 3k+1 cũng thuộc A.

Yêu cầu: Giả sử tập A đã được sắp xếp theo thứ tự tăng dần Hãy tìm phần tử thứ N của tập A

Dữ liệu vào: Vào từ file văn bản BAI4.INP gồm 1 dòng duy nhất chứa số N Kết quả: Ghi ra file văn bản BAI4.OUT gồm 1 số duy nhất là phần tử thứ

N của tập A tìm được.

Ví dụ:

Rằng buộc:

- Có 3 số test tương ứng với 3 số điểm có N ≤ 104

- Có 23 số test tương ứng với 23 số điểm có 104 < N ≤ 106

Phân tích ý tưởng:

Dùng mảng đánh dấu chỉ số Với số liệu bài này, ta chỉ cần duyệt đến nmax = 2.106, tức là đánh dấu từ 1 -> 2.106, (có thể nmax đến 107 cho thoải mái nè) Đầu tiên ta đánh dấu bằng False hết, và gán A[1]=true Bắt đầu duyệt từ 1 bằng một vòng for, gặp đánh dấu nào = true thì tăng biến đếm lên 1, đồng thời, ngay lập tức gán a[i*2+1]:= true và a[i*3+1]:= true Vòng for cứ chạy đến khi nào đến n là đủ, rồi break nó ra (Tuy nhiên thực tế thì for không cần đến nmax, mà đến tầm khoảng nmax div 2 là dư sức đủ rồi.)

Đánh giá thời gian chạy: Việc dùng mảng đánh dấu này chỉ cần dùng 1 vòng

for Độ phức tạp O(n) Số lần chạy không quá 2.n Thời gian chạy không quá 0.5s

Code tham khảo:

const fi='bai4.inp';

Trang 10

fo='bai4.out';

maxn= round(7e7); // Nghĩa là maxn = 7.107

var i,n: longint;

a: array[1 maxn] of boolean;

dem: longint;

BEGIN

assign(input, fi); reset(input);

assign(output, fo); rewrite(output);

readln(n);

for i:= 1 to maxn do a[i]:= false;

a[1]:= true;

for i:=1 to maxn div 2 do

if a[i]=true then

begin

if 2*i+1<= maxn then a[i*2+1]:= true;

if 3*i+1<= maxn then a[i*3+1]:= true;

end;

dem:= 0;

for i:= 1 to maxn do

if a[i]=true then

begin

inc(dem);

if dem = n then

begin

writeln(i);

break;

end;

end;

close(output);

END

Trang 11

Bài 4 : Đếm số lượng ( Đề thi học sinh giỏi Bình Phước 2012-2013)

Xét dãy gồm N số nguyên dương A1, A2, …, AN và số nguyên X cho trước Hãy

đếm số cặp (Ai, Aj) thỏa mãn các điều kiện:

o Ai + Aj = X

o 1 £ i < j £ N

Dữ liệu vào từ tệp văn bản SOLUONG.INP:

o Dòng đầu tiên chứa số nguyên dương N với 1 < N ≤ 106

o Dòng tiếp theo chứa N số nguyên A1, A2, , AN với 0 < Ai < 105, 1 ≤ i ≤

N Hai số kề nhau cách nhau một khoảng trắng

o Dòng cuối cùng chứa số nguyên dương X < 106

Dữ liệu ra ghi vào tệp văn bản SOLUONG.OUT: Số lượng cặp (Ai, Aj) thỏa mãn điều kiện trên

Ví dụ:

9

5 12 7 11 9 1 2 3 11 13

3

Phân tích ý tưởng: Với số liệu 106 ta giải quyết bài này như sau, trước hết, sắp xếp lại dãy, đồng thời cho biết tần suất xuất hiện của từng số trong dãy vừa sắp xếp

Kết quả dùng quy tắc nhân các tần suất của cặp giá trị thỏa mãn

Vì liên quan đến tần suất xuất hiện của các số nên ta dùng thuật toán sắp xếp theo phân phối đơn giản và hiệu quả như sau: Xây dựng mảng C , với C[i] để đánh dấu số lần xuất hiện của i

fillchar(c, sizeof(c), 0); {Khởi tạo mảng C}

for i := 1 to n do inc(c[a[i]]);

procedure sap_xep;

var

Trang 12

i: longint;

begin

fillchar(c, sizeof(c), 0);

for i := 1 to n do

inc(c[a[i]]);

fillchar(a, sizeof(a), 0);

n := 0;

for i := 1 to _value_max do

if c[i] <> 0 then

begin

inc(n);

a[n] := i;

end;

end;

Sau khi sắp xếp, các phần từ i

Độ phức tạp của thuật toán sắp xếp là: O(Max(N,K));

Code tham khảo:

const

_n_max = 1000000;

_value_max = 100000;

fi = 'soluong.inp';

fo = 'soluong.out';

var

a: array[1 _n_max] of longint;

c: array[1 _value_max] of longint;

x: longint;

n: longint;

sl: longint;

procedure nhap;

var

Trang 13

i: longint;

begin

assign(input, fi); reset(input);

readln(n);

for i := 1 to n do read(a[i]);

readln(x);

close(input);

end;

procedure xuat;

begin

assign(output, fo); rewrite(output);

write(sl);

close(output);

end;

procedure sap_xep;

var

i: longint;

begin

fillchar(c, sizeof(c), 0);

for i := 1 to n do

inc(c[a[i]]);

fillchar(a, sizeof(a), 0);

n := 0;

for i := 1 to _value_max do

if c[i] <> 0 then

begin

inc(n);

a[n] := i;

end;

end;

Trang 14

procedure xu_li;

var

i, j: longint;

begin

sap_xep;

sl := 0;

i := 1;

j := n;

while i < j do

begin

if a[i] + a[j] = x then

sl := sl + c[a[i]] * c[a[j]];

if a[i] + a[j] > x then

dec(j)

else

inc(i);

end;

if (2 * a[i] = x) and (c[a[i]] > 1) then

sl := sl + c[a[i]] * (c[a[i]] - 1) div 2;

end;

BEGIN

nhap;

xu_li;

xuat;

END

MẢNG HẰNG –MỘT DẠNG ĐẶC BIỆT CỦA MẢNG ĐÁNH DẤU

Mảng hằng là một dạng đặc biệt, thi thoảng vẫn được sử dụng để tính toán nhanh một số bài toán có số liệu lớn, chạy tốn nhiều thời gian Khi sử dụng mảng hẳng thì bài toán trở nên khá nhẹ nhàng

Nó cũng được xem là một dạng của mảng đánh dấu,

Trang 15

Để hiểu có thể làm rõ vấn đề hơn, chúng ta đi xét một ví dụ sau:

Năm 1973, một nhà toán học đưa ra khái niệm Độ bền của một số nguyên không

âm n được định nghĩa như sau: Nếu N có một chữ số thì độ bền của n bằng 0 -Nếu N có từ 2 chữ số trở lên thì độ bền của n bằng độ bền của số nguyên là tích các chữ số của n cộng 1

Yêu cầu: Cho số nguyên N, Tìm số bé hơn n có độ bền lớn nhất

(giới hạn N < 2.10 9 )

Dứ liệu vào: file văn bản Doben.inp Mỗi dòng ghi một số nguyên dương N Kết quả: Ghi ra file văn bản Doben.out Mỗi dòng ghi một số tương ứng tìm được.

Ví dụ: Với n = 100 thì in ra kết quả: So be hon 100 co do ben lon nhat la: 77

• Giải thích:

Doben(77)=Doben(49)+1=Doben(36)+1+1=Doben(18)+1+1+1

= Doben(8)+1+1+1+1=0+1+1+1+1=4

Hướng giải quyết vấn đề:

Ta hoàn toàn có thể viết hàm tìm độ bền của một số n (như hình bên dưới):

Và chương trình in ra số k lớn nhất có độ bền bằng i như sau:

uses crt;

function doben(n:longint):longint;

var t:longint;

begin

Ngày đăng: 19/05/2021, 20:57

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w