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

SKKN một số phương pháp tiếp cận bài toán, làm giảm độ phúc tạp của thuật toán

20 99 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 145,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

Lý do chọn đề tài Bài toán trong tin học là bài toán xuất phát từ bài toán thực tế, việc xây dựng giải thuật để giải bài toán tin học bằng máy tính phải bảo đảm những tính chất cơ bản c

Trang 1

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM

Độc lập – Tự do – Hạnh phúc



SÁNG KIẾN KINH NGHIỆM :

MỘT SỐ PHƯƠNG PHÁP TIẾP CẬN BÀI TOÁN, LÀM GIẢM ĐỘ PHỨC TẠP CỦA THUẬT TOÁN

Trang 2

MỤC LỤC

MỤC LỤC 2

I MỞ ĐẦU 3

1 Lý do chọn đề tài .3

2 Điểm mới của đề tài 3

3 Phạm vi áp dụng đề tài 3

II NỘI DUNG 4

1 Thực trạng vấn đề cần nghiên cứu 4

2 Biện pháp tiến hành 4

Dạng 1: Một số bài toán duyệt cơ bản 5

Bài 1: Sắp xếp 5

Bài 2: Tích lớn nhất 6

Bài 3: Tổng 2 số lớn 9

Dạng 2: Sử dụng thuật toán chia để trị để giảm độ phức tạp của bài toán 14

Bài 1: Chọn dãy số 14

Bài 2: Hieu 16

3 Đánh giá kết quả thực nghiệm 18

III KẾT LUẬN 19

IV TÀI LIỆU THAM KHẢO 20

Trang 3

I PHẦN MỞ ĐẦU

1 Lý do chọn đề tài

Bài toán trong tin học là bài toán xuất phát từ bài toán thực tế, việc xây dựng giải thuật để giải bài toán tin học bằng máy tính phải bảo đảm những tính chất

cơ bản của thuật toán, trong các tính chất chất cơ bản đó thì yêu cầu về thuật toán sau khi đã được cài đặt chương trình trên máy tính bằng ngôn ngữ lập trình thì thuật toán phải chạy nhanh, tốn ít thời gian và bộ nhớ Đây là yêu cầu rất quan trọng đối với những học sinh tham gia đội tuyển học sinh giỏi Điều này được thể hiện rất rõ trong nhiều bài toán qua các đề thi học sinh giỏi Tỉnh trong những năm gần đây Trong những bài toán này thường yêu cầu của nó được chia thành nhiều mức nhằm mục đích phân loại được trình độ của học sinh Mức độ khó của bài toán phụ thuộc rất nhiều vào vào độ lớn của dữ liệu đầu vào nên ngoài vấn đề về việc chọn được giải thuật để cho kết quả đúng đòi hỏi chương trình phải chạy hết các bộ test lớn bằng một đơn vị thời gian đã được quy định trong từng bài toán mới đạt được mức điểm tối đa Để đảm bảo được yêu cầu đó, đòi hỏi giải thuật để giải quyết bài toán phải có độ phức tạp tính toán càng nhỏ càng tốt

Khi tiếp cận với một bài toán, tùy theo trình độ của từng học sinh mà mỗi học sinh có cách phân tích bài toán và lựa chọn thuật toán khác nhau để giải nó Đối với những học sinh có trình độ chưa cao, chưa có khả năng phân tích bài toán tốt thường lựa chọn những thuật toán đơn giản, có độ phức tạp lớn đồng nghĩa với số điểm nhận được thấp hơn Để có kết quả tốt hơn, đòi hỏi học sinh phải có trình độ cao hơn, kỹ năng phân tích bài toán tốt hơn để tìm ra những thuật toán tốt có độ phức tạp tính toán nhỏ Đây là vấn đề mà mỗi giáo viên tin học tham gia bồi dưỡng đội tuyển học sinh giỏi luôn coi trọng Đòi hỏi giáo viên phải tìm ra những phương pháp phù hợp cho từng đối tượng học sinh để giúp học sinh từng bước tiếp cận bài toán tin học từ đơn giản đến phức tạp hơn phù hợp với năng lực của mình để dành được điểm số cao nhất có thể

Với những lý do trên tôi chọn đề tài sáng kiến kinh nghiệm với nhan đề:

“Một số phương pháp tiếp cận bài toán, làm giảm độ phức tạp của thuật toán”

2 Điểm mới của đề tài:

Tính mới của đề tài thể hiện việc thu thập một số bài toán sữ dụng trong bồi dưỡng học sinh giỏi và một số đề thi cấp tỉnh từ đó phân loại các bài toán theo các dạng, phân tích các dạng bài toán theo từng chủ đề dựa trên những thuật toán cơ bản đến nâng cao để tìm cách xây dựng giải thuật tốt nhất cho việc giải những dạng bài toán giống nhau và dạng tương tự

3 Phạm vi áp dụng đề tài:

Đề tài được áp dụng trong bồi dưỡng HSG 11 năm học 2017-2018

Trang 4

II NỘI DUNG

1.Thực trạng vấn đề cần nghiên cứu:.

Trong quá trình bồi dưỡng học sinh gỏi vấn đề về phát triển kỹ năng cho học sinh đòi hỏi người giáo viên phải thường xuyên tìm tòi phương pháp, phải

có những hệ thống bài tập từ đơn giản đến nâng cao để phát triển kỹ năng thường xuyên cho học sinh mới đáp ứng yêu cầu ngày càng cao của bài toán trong tin học

Khi tiếp xúc với một bài toán trong tin học, học sinh thường cố gắng tìm

ra thuật toán tốt nhất để giải quyết trọn vẹn bài toán, đối với những bài toán đơn giản học sinh có thể tìm ra ngay thuật toán nhưng đối với những bài toán khó đặc biệt là những bài toán có dữ liệu đầu vào lớn thì việc tìm ra thuật toán tối ưu nhất để đạt được số điểm tối đa thường là rất khó Học sinh không nghĩ được rằng nếu sử dụng những giải thuật đơn giản để giải quyết các bài toán khó thì cho dù không đạt được số điểm tối đa nhưng cũng có được một điểm số ở một mức độ nào đó phù hợp với khả năng của mình để tránh điểm không Đây là và kinh nghiệm cần thiết đối với những học sinh giỏi tham gia những ký thi HSG môn tin học

Trước khi áp dụng sáng kiến kinh nghiệm kết quả khảo sát cho các học sinh giỏi khối 11 là:

- Mức điểm giỏi: 0%

- Mức điểm khá: 30%

- Mức điểm TB: 55%

- Mức điểm yếu: 15%

Để rèn luyện kỹ năng tiếp cận bài toán nhằm giúp học sinh có được kinh nghiệm tiếp cận bài toán khó bằng nhiều thuật toán theo từng bước từ đơn giản đến phức tạp phù hợp với khả năng của mình để có được điểm số tốt nhất đòi hỏi người giáo viên cần rèn luyện kỹ năng tiếp cận bài toán để làm giảm độ phức tạp của bài toán

2 Biện pháp tiến hành

Để giải một bài toán tin học trên máy tính chúng ta thường phải thực hiện theo từng bước:

Bước đầu tiên và là bước quan trọng nhất là hiểu rõ nội dung bài toán.

Đây là yêu cầu quen thuộc đối với những người làm toán Để hiểu bài toán theo cách tiếp cận của tin học ta phải gắng xây dựng một số ví dụ phản ánh đúng các yêu cầu đề ra của đầu bài rồi thử giải các ví dụ đó để hình thành dần những hướng đi của thuật toán

Bước thứ hai là dùng một ngôn ngữ quen thuộc, tốt nhất là ngôn ngữ toán

học đặc tả các đối tượng cần xử lí ở mức độ trừu tượng, lập các tương quan, xây dựng các hệ thức thể hiện các quan hệ giữa các đại lượng cần xử lí để có được thuật toán tốt nhất có thể

Trang 5

Bước thứ ba là xác định cấu trúc dữ liệu để biểu diễn các đối tượng cần

xử lí cho phù hợp với các thao tác của thuật toán

Trong những bước tiếp theo ta tiếp tục làm mịn dần các đặc tả theo trình

tự từ trên xuống, từ trừu tượng đến cụ thể, từ đại thể đến chi tiết

Bước cuối cùng là sử dụng ngôn ngữ lập trình đã chọn để viết chương

trình hoàn chỉnh Ở bước này ta tiến hành theo kĩ thuật đi từ dưới lên, từ những thao tác nhỏ đến các thao tác tổ hợp

Sau khi nhận được chương trình ta cho chương trình chạy thử với các dữ liệu lấy từ các ví dụ đã xây dựng ở bước đầu tiên

Chúng ta sẽ vận dụng cách tiếp cận trên để giải một số bài toán cụ thể trong đó chủ yếu là bước thứ hai để tìm thuật toán tốt nhất Sau đây là một số phương pháp cụ thể để tối ưu độ phức tạp tính toán của giải thuật

Dạng 1: Một số bài toán duyệt cơ bản

Bài 1: Sắp xếp

Cho một dãy X gồm N số nguyên trong phạm vi từ -10000 đến 10000 (1

N  65000) Hãy sắp xếp dãy số này theo thứ tự giảm dần

Dữ liệu vào cho trong file văn bản SORT.INP trong đó dòng đầu chứa số

N Dòng thứ 2 chứa N số i trong dãy X, các số cách nhau một khoảng trắng

Kết quả ghi ra file văn bản với tên SORT.OUT trong đó lần lượt ghi ra các phần tử của dãy X đã được sắp xếp

Ví dụ:

4

3 4 3 5

5 4 3 3

Lời giải:

+ Nếu sử dụng thuật toán sắp xếp thông thường như Quick Sort thì độ phức tạp tính toán cũng chỉ là O(Nlog2N)

+ Đối với bài này, để thuật toán chạy nhanh hơn ta sử dụng phương pháp sắp xếp đếm phân phối với độ phức tạp giảm tới là O(N) khi các số trong dãy là đôi một khác nhau

Gọi Di là số lần xuất hiện của số có giá trị bằng i trong dãy

Như vậy mỗi lần đọc số có giá trị i trong dãy ta tăng Di lên 1 đơn vị

Đoạn chương trình sau sẽ đọc tất cả các số trong dãy để tạo mảng D chứa tần suất xuất hiện của các số trong dãy số đã cho:

For i:=1 to N do

Begin

Read(x);

D[x]:=D[x]+1;

End;

Trang 6

Vấn đề còn lại là dựa vào mảng D để in ra các số trong dãy theo thứ tự giảm dần Đoạn chương trình sau in ra dãy số đã cho theo thứ tự giảm dần

For i:=10000 downto -10000 do

If D[i]>0 then

For j:=1 to D[i] do writeln(i);

Chương trình:

Const fi='sort.inp';

fo='sort.out';

Var D: array[-10000 10000] of Word;

n, i: longint;

f, f1: text;

BEGIN

Assign(f,fi);

Reset(f);

Assign(f1,fo);

Rewrite(f1);

Fillchar(D,sizeof(D),0);

Readln(f,n);

For i:=1 to n do

Begin

Readln(f,x);

Inc(D[x]);

End;

For i:=10000 downto -10000 do

If D[i]>0 then

For j:=1 to D[i] do write(f1,i,' ');

Close(f);

Close(f1);

END

Bài 2: Tích lớn nhất

Cho một dãy gồm N số nguyên Hãy tìm 3 số trong dãy với tích T của chúng là lớn nhất

Dữ liệu: Vào từ file văn bản TICHMAX.INP:

- Dòng đầu ghi số N (3N10000)

- Dòng thứ hai chứa N số nguyên có giá trị tuyệt đối không vượt quá 1000

Kết quả: Ghi ra file văn bản TICHMAX.OUT một số duy nhất T.

Trang 7

Ví dụ:

9

3 5 1 7 9 0 9 -3 10

810

Lời giải:

+ Cách tiếp cận tầm thường: Sử dụng thuật toán duyệt vét cạn với độ phức tạp O(n3) như sau:

KQ:=MinInt;

For i:=1 to n-2 do

For j:=i+1 to n-1 do

For k:=1 to n do

If a[i]*a[j]*a[k] > KQ then KQ:=a[i]*a[j]*a[k];

+ Chúng ta có cách tiếp cận sau đây có thể đưa độ phức tạp của thuật toán

về O(n)

Nếu tích lớn nhất của 3 phần tử bao gồm:

- Ba số dương: Ba số này phải lần lượt là số lớn nhất, nhì, ba trong dãy

- Một số âm và hai số dương: Dãy phải gồm đúng hai số dương, vì nếu không ta có thể lấy tích ba số dương để đạt giá trị lớn hơn Ta cũng suy ra ba số này phải là ba số lớn nhất, nhì, ba trong dãy

- Hai số âm và một số dương: Số dương phải là số lớn nhất và hai số âm phải là hai số nhỏ nhất, nhì trong dãy

- Ba số âm: Dãy phải gồm toàn số âm, vì nếu có một số dương ta cũng có thể lấy tích của số dương đó và hai số âm để thu được tích dương Ta cũng suy

ra được ba số cần tìm phải là ba số lớn nhất, nhì, ba của dãy

Vậy suy ra, T=max(max1*max2*max3,min1*min2*max1), với max1, max2, max3, min1, min2 lần lượt là số lớn nhất, nhì, ba và số nhỏ nhất, nhì của dãy

Ta có thể đọc qua lần lượt các số của dãy và cập nhật max1, max2, max3, min1, min2 mà không cần lưu lại dãy số Đoạn lệnh dưới đây cập nhật min1, min2 khi đọc vào một số mới x:

If (x<=min1) then

Begin

min2:=min1; {min1 giờ trở thành số bé thứ hai}

min1:=x; {số bé nhất là x}

End

Else If (x<=min2) then min2:=x; {số bé thứ hai là x}

Việc cập nhật max1, max2, max3 được thực hiện hoàn toàn tương tự

Trang 8

Chương trình

Program Tich;

Const fi='tichmax.inp';

fo='tichmax.out';

Var max1,max2,max3,min1,min2,x:integer;

i,n, kq:longint;

f, f1: Text;

Function max(a,b:longint):longint;

Begin

if a>b then max:=a

else max:=b;

End;

BEGIN

Assign(f,fi);

Reset(f);

Assign(f1,fo);

Rewrite(f1);

max1:=-1000; max2:=max1; max3:=max1; min1:=-1000; min2:=min1;

Readln(F,n);

for i:=1 to n do

Begin

Read(f,x);

If (x>=max1) then

Begin

max3:=max2;

max2:=max1;

max1:=x;

End

Else

If (x>=max2) then

Begin

max3:=max2;

max2:=x;

End

Else if (x>=max3) then max3:=x;

If (x<=min1) then

Begin

min2:=min1;

min1:=x;

End

Trang 9

Else if (x<=min2) then min2:=x;

End;

kq:=max(max1*max2*max3,min1*min2*max1);

writeln(f1,kq);

close(f1);

close(f);

END

Bài 3: Tổng 2 số lớn:

Cho số nguyên X có M chữ số, số nguyên Y có N chữ số

Yêu cầu:

Tính tổng của X và Y

Dữ liệu vào: Cho trong tệp SUM.INP với cấu trúc.

Dòng 1: Ghi hai số M, N (1<M,N<30000), hai số cách nhau một dấu cách Dòng 2: Ghi số X

Dòng 3: Ghi số Y

Dữ liệu ra: Ghi ra tệp SUM.OUT với cấu trúc

Dòng 1: Ghi tổng của X và Y

Ví dụ:

3 4 123 1234

1357

Lời giải:

Sử dụng hai xâu để lưu trữ giá trị của hai số lớn

Var so1, so2: String;

Chương trình:

Program CongSL;

Const fi='SUM.INP';

fo='SUM.OUT';

Var s1,s2,s3:String;

i , m,n:Integer;

Procedure Docdl;

Var f:text;

ch:char;

Begin

Assign(f,fi);

Reset(f);

Readln(f,m,n);

Readln(f,s1);

Readln(f,s2);

Trang 10

Function Cong(a,b : String): String;

Var tong, nho, i, x, y,code : integer;

c, s : String;

Begin

nho:=0; c:='';

While length(a)<length(b) do

a:='0'+a;

While length(b)<length(a) do

b:='0'+b;

For i:=length(a) downto 1 do

Begin

Val(a[i],x,code);

Val(b[i],y,code);

tong:=x + y + nho;

nho:=tong div 10;

Str(tong mod 10,s);

c:=s+c;

End;

If nho>0 then c:='1'+c;

Cong:=c;

End;

Procedure Ghidl;

Var f: text;

Begin

Assign(f,fo); Rewrite(f);

S3:=cong(s1,s2);

Write(f,s3); Close(f);

End;

BEGIN

Docdl;

Ghidl;

END

Với phương pháp này, chúng ta chỉ tính toán được với những con số có không quá 255 chữ số Vậy với bài toán khi những con số có đến 30000 chữ số không thể giải quyết được Thêm vào đó nếu số lượng chữ số là 255 thì số lượng phép toán chuyển đổi từ ký tự thành số và ngược lại là rất nhiều, điều đó làm tăng thời gian thực hiện của chương trình

Cách tiếp cận sau đây sẽ giảm độ phức tạp của bài toán:

Chúng ta vẫn sử dụng mảng kiểu số để lưu trữ một số có nhiều chữ số Mảng có 3.500 phần tử mỗi phần tử kiểu longint Mỗi phẩn tử của mảng lưu trữ

Trang 11

một số nguyên có 9 chữ số Với phương pháp này ta đã tính toán được trên hai

số có 30.000 chữ số với thời gian nhanh nhất Khi cộng (hoặc trừ) hai số có 30.000 chữ số, ta chỉ cần sử sụng 3.100 phép cộng (hoặc phép trừ) và việc cải tiến thủ tục đọc dữ liệu sẽ giúp thuật toán hoàn toàn loại bỏ được các thao tác chuyển đổi ký tự thành số

Chương trình:

Program CongSL;

Const fi='SUM.INP';

fo='SUM.OUT';

Var A,B: Array[0 3500] of longint;

i, m,n:Integer;

Procedure Docdl;

Var f:text;

s:String[9]; ch:char;

dua,dub,code:Integer;

Begin

Fillchar(A,sizeof(A),0);

Fillchar(B,sizeof(B),0);

Assign(f,fi);

Reset(f);

Readln(f,m,n);

dua:=m mod 9;

dub:=n mod 9;

m:=m div 9;

n:=n div 9;

If m>n then

Begin

s:='';

For i:=1 to dua do

Begin

read(f,ch);

s:=s+ch;

End;

Val(s,A[1],code);

For i:= 2 to m+1 do

Begin

Read(f,s);

Val(s,A[i],code);

End;

Readln(f);

Trang 12

s:='';

For i:=1 to dub do

Begin

read(f,ch);

s:=s+ch;

End;

Val(s,B[m-n+1],code);

For i:= m-n+2 to m+1 do Begin

Read(f,s);

Val(s,B[i],code);

End;

End

Else

Begin

s:='';

For i:=1 to dua do

Begin

read(f,ch);

s:=s+ch;

End;

Val(s,A[n-m+1],code);

For i:= n-m+2 to n+1 do Begin

Read(f,s);

Val(s,A[i],code);

End;

Readln(f);

s:='';

For i:=1 to dub do

Begin

read(f,ch);

s:=s+ch;

End;

Val(s,B[1],code);

For i:= 2 to n+1 do

Begin

Read(f,s);

Val(s,B[i],code);

End;

End;

Trang 13

Close(f);

End;

Procedure XL;

Begin

If m>n then n:=m;

For i :=n+1 downto 1 do

Begin

B[i]:=B[i]+A[i];

If B[i] div 1000000000 = 1 then Begin

B[i]:=B[i] mod 1000000000; inc(B[i-1]);

End;

End;

End;

Procedure Ghidl;

Var f:text;

Begin

Assign(f,fo);

Rewrite(f);

If B[1]<>0 then Write(f,B[1]);

For i:=2 to n+1 do

If B[i] =0 then Write(f,'000000000') Else Write(f,B[i]);

Close(f);

End;

BEGIN

Docdl; Xl;

Ghidl;

END

Ngày đăng: 13/11/2019, 11:00

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