Tài liệu này là chuyên đề bồi dưỡng giáo viên cốt cán môn tin học bậc THCS của Sở GDĐT. Nội dung tập trung bổ sung các kiến thức nâng cao trong kỹ thuật lập trình Pascal phục vụ dạy HS giỏi. Thuật toán đệ qui quay lui, nhánh cận được sử dụng giải các bài toán: Cân vật, rót nước, bảng số, vòng trong nguyên tố...Các thuật toán đồ thị đơn giản ứng dụng trong giải bài toán tối ưu.
Trang 1Chuyên đề 3 BỒI DƯỠNG HỌC SINH GIỎI
I BỔ SUNG KIẾN THỨC CƠ BẢN VỀ NGÔN NGỮ LẬP TRÌNH PASCAL 1.1 Sự cần thiết phải có kiểu mảng
Xét bài toán 1 “ Nhập vào nhiệt độ trung bình của mỗi ngày trong tuần, đưa
ra nhiệt độ trung bình của tuần và số ngày trong tuần có nhiệt độ trung bình cao hơnnhiệt độ TB của tuần”
Cần khai báo bao nhiêu biến lưu trữ nhiệt độ ngày ? Cần 7 biến
Nếu yêu cầu bài toán không là 7 ngày mà là 1 năm thì có giải quyết đượckhông?
Bản chất thuật toán không có gì thay đổi nhưng phải khai báo quá nhiều biến,chương trình phải viết nhiều câu lệnh nên rất dài
Để khắc phục những hạn chế trên, NNLT ghép chung 7 biến trên thành mộtdãy, đặt cho nó chung một tên và đánh dấu cho mỗi phần tử là một chỉ số Đó chính
là kiểu dữ liệu mảng
1.2 Kiểu mảng một chiều
Khái niệm: Mảng một chiều là một dãy hữu hạn các phần tử có cùng kiểu dữ
liệu
Mảng là kiểu dữ liệu có cấu trúc
Khi xây dựng kiểu mảng một chiều cần xác định:
- Tên mảng một chiều
- Số lượng phần tử trong mảng
- Kiểu dữ liệu của phần tử
- Cách khai báo biến mảng một chiều
- Cách truy cập vào từng phần tử của mảng
a Khai báo
Cách 1: Khai báo biến trực tiếp:
Var <Tên biến mảng> : array [kiểu chỉ số] of <kiểu phần tử>;
Cách 2: khai báo biến gián tiếp
- Khai báo kiểu dữ liệu mảng
Type <Tên kiểu mảng> = array [Kiểu chỉ số] of <Kiểu phần tử>;
Var <Tên biến mảng> : <tên kiểu mảng>;
Trong đó:
- Tên kiểu mảng, Tên biến mảng do người lập trình đặt theo qui tắc đặt tên
Trang 2- Kiểu chỉ số thường là một đoạn số nguyên liên tục n1 n2 (n1<=n2) n1 gọi làchỉ số đầu, n2 gọi là chỉ số cuối Kiểu chỉ số phải có kiểu đếm được.
- Kiếu phần tử là các kiểu dữ liệu chuẩn và kiểu có cấu trúc ngoại trừ kiểu file
Ví dụ:
+ Khai báo biến lưu trữ nhiệt độ từng ngày trong 1 năm
C1: Var NhietDo= array[1 366] of Real;
C2: Type Kmang1= array[1 366] of Real;
b Qui tắc tham chiếu đến phần tử mảng
<Tên biến mảng>[<Chỉ số>]
Trong đó: n1<= Chỉ số <=n2Lưu ý: chỉ số <n1 hoặc > n2 sẽ saiKhi tham chiếu đến phần tử xem như 1 biến có kiểu dữ liệu của phần từ
Ví dụ:
+ Nhiệt độ ngày đầu tiên: NhietDo[1]
+ Nhiệt độ ngày 1 tháng 2: NhietDo[32]
+ Xem nhiệt độ từ ngày 1 đến ngày n
For i:= 1 to n do Write(Nhietdo[i]:6:2)
+ Tính tổng nhiệt độ trong năm
Tong:=0;
For i:= 1 to n do Tong:= Tong +Nhietdo[i];
+ Đếm số ngày có nhiệt độ lớn hơn nhiệt độ TB
Trang 3Var <tên mảng>: array [Kiểu chỉ số dòng, kiểu chỉ số cột] of <kiểu phần tử>;
Cách 2: Khai báo gián tiếp:
Type <tên kiểu mảng>= array [Kiểu chỉ số dòng, kiểu chỉ số cột] of <kiểu phần tử>;
Var <tên mảng>:<tên kiểu mảng>;
Ví dụ: Cho mảng a gồm 5 dòng, 4 cột, kiểu số nguyên ta khai báo như sau:
Type mang=array [1 5,1 4] of integer;
c Biến đổi các số dương trong mảng thành số 1, các số còn lại thành số 0
d Tìm giá trị lớn nhất (bé nhất), tìm vị trí phần tử đầu tiên có giá trị bé nhất (số dương bé nhất, số âm lớn nhất, vị trí đầu tiên của số lẻ, vị trí cuối cùng của
số chẵn)
e Nhập một số nguyên x Tìm vị trí xuất hiện của x nếu có, ngược lại thì thông báo không có số này ( đếm xem số này xuất hiện bao nhiêu lần trong mảng)
f Đổi chỗ giữa số lẻ đầu tiên và số chẵn cuối cùng của mảng nếu có
g Tìm vị trí của số có giá trị “gần” với giá trị trung bình cộng nhất Khoảng cách giữa 2 số là trị tuyệt đối của hiệu 2 số đó
2: Viết chương trình nhập n, Sinh một dãy ngẫu nhiên n số từ 1 đến 100 sau đó xử
lý
Hàm random(100): sinh số từ 0 đến 100
a Nhập vị trí k, xóa phần tử ở vị trí thứ k Đưa dãy đã xóa ra màn hình
b Nhập vị trí k và một số x tùy ý Chèn số x vào vị trí k Đưa dãy đã chèn ra màn hình
c Sắp xếp để thu được dãy tăng dần, (dãy giảm dần) Đưa dãy đã sắp xếp ra màn hình Cho biết có bao nhiêu lần hoán chuyển 2 phần tử trong dãy khi sắpxếp
Trang 4d Nhập 0<k<n+1, sắp xếp các số từ vị trí 1 đến k thành dãy tăng, từ vị trí k+1 đến n thành dãy giảm dần.
e Sắp xếp thành một dãy tăng dần Nhập số nguyên x,
chèn x vào tại vị trí thích hợp để dãy vẫn là một dãy
tăng Đưa dãy đã chèn ra màn hình
f Kiểm tra dãy có là dãy tăng không? (dãy là các số lẻ,
dãy là cấp số cộng, là dãy fibonacy, là các số nguyên
d Tìm phần tử lớn nhất và phần tử nhỏ nhất hoán đổi giá trị hai phần tử này
2 Nhập n <20, đưa ra ma hình ma trận hình xoắn ốc ví dụ với n =5 như hình trên
5 Nhập n, đưa ra màn hình tam giác pascal Với n =5 đưa ra kết quả sau
Trang 5- Xâu là một dãy kí tự trong bảng mã ASCII.
- Mỗi kí tự gọi là một phần tử của xâu
- Số lượng kí tự trong xâu được gọi là độ dài của xâu
- Xâu có độ dài bằng 0 gọi là xâu rỗng
- Tham chiếu tơí phần tử trong xâu được xác định thông qua chỉ số của phần tử trong xâu
- Chỉ số phần tử trong xâu được đánh số là 1
Ví dụ: Những đối tượng cần khai báo kiểu xâu: Họ tên, tên môn học, địa chỉ,quê quán
2.3 Khai báo biến xâu
Var <tên biến> : String [độ dài lớn nhất của xâu ] ;
+ Tham chiếu tới từng kí tự xâu: <tên biến xâu>[chỉ số]
+ Ghép xâu: + : Ghép nhiều xâu lại thành 1 xâu
VD: ‘Ha’ +’ noi’ cho kết quả ‘Ha noi’
+ Length(s): độ dài xâu
+ Delete(s, 5, 2): xóa xâu s từ vị trí thứ 5 và xóa 2 ký tự
+ Insert(S1, S2, vt) : chèn S1 vào S2 bắt đầu từ vị trí vt
Trang 6+ Copy(S, vt, N) tạo xâu gồm N kí tự liên tiếp bắt đầu từ vt của xâu S + Pos(S1,S2): trả về vị trí xuất hiện đầu tiên của xâu S1 trong xâu S2
2 Nhập 1 xâu họ tên đầy đủ (theo cách đặt tên nguời việt) Sau đó in ra màn
hình Họ,Họ đệm, Tên của nguời đó
3 Nhập vào 1 xâu s kiểm tra xâu đã nhập có đối xứng không
4 Đọc một loạt các kí tự từ bàn phím cho đến khi ấn Enter thì ngừng nhập In ra
5 Xét một dãy các chữ số A nhận được từ cách ghép liên tiếp các chữ số
nguyên dương liên tiếp tăng dần bắt đầu từ 1,2,3, 4 đến số m cho trước (m>30 và m<=1000) Cho biết một số nguyên dương N hãy tìm vị trí đầu tiên của số N trong dãy A Biết rằng vị trí trong dãy A được tính từ 1 nếu không tìm thấy số N trong dãy A thì trả lời vị trí là 0
Trang 7 Có thể dùng nhiều mảng 1 chiều để lưu trữ, mỗi mảng lưu trữ 1 cột
khi lấy thông tin người thứ 3 cần phải làm với nhiều mảng Bất tiện
Ngôn ngữ lập trình bậc cao có cách tốt hơn để quản lý dữ liệu trên gọi là Kiểubản ghi Một hàng là 1 biến có kiểu bản ghi, mỗi cột thể hiện thuộc tính của đốitượng gọi là Trường
3.2 Một số khái niệm
- Kiểu bản ghi (Record) là một kiểu dữ liệu có cấu trúc
- Một bản ghi gồm các thành phần (gọi là trường), các trường có thể thuộc cáckiểu dữ liệu khác nhau
- Kiểu bản ghi dùng để mô tả nhiều đối tượng có cùng một số thuộc tính
- Khi làm việc với bản ghi cần biết;
+ Tên kiểu bản ghi;
+ Tên các trường;
+ Kiểu dữ liệu của mỗi trường;
+ Cách khai báo biến;
+ Cách tham chiếu đến trường
3.3 Khai báo
Khai báo kiểu:
Type <tên kiểu bản ghi> = record
<tên trường 1>:<kiểu trường 1>;
………
<tên trường n>:<kiểu trường n>;
End;
Khai báo biến:
Var <tên biến>: <tên kiểu bản ghi>;
Trang 8Var hs, hs1, hs2: HocSinh;
+ Khai báo biến lưu trữ dữ liệu cho cả lớp
Var Lop: array [1 50] of HocSinh;
3.4 Tham chiếu đến từng trường
Khi làm việc với bản ghi tức là làm viêc với từng thành phần của nó
Qui tắc tham chiếu đến từng trường
<tên biến bản ghi> <tên trường>
Hoăc
WITH <Tên biến kiểu bản ghi> DO <Câu lệnh tác động đến tên trường>;
Ví dụ:
+ Biến hs: hs.HoTen, hs.toan
+ Biến Lop chúng ta cần tham chiếu đến phần tử mang Lop[i] sau đó tham chiếuđến trường: Lop[i].HoTen
3.5 Gán giá trị
Có hai cách để đưa giá trị cho bản ghi
a) Dùng lệnh gán trực tiếp: A:=B
Yêu cầu A, B cùng kiểu
b) Đưa giá trị cho từng trường tùy thuộc vào kiểu dữ liệu của từng trường
- Nếu trường có kiểu dữ liệu cơ sở ta có thể gán như sau:
<Tên biến> <Tên trường>:= <biểu thức>;
Readln(<Tên biến> <Tên trường>);
Ví dụ: Cho biến HS lưu trữ thông tin như sau:
Hs.NgaySinh:=’10-10-1995’
Hs Toan:=8; Hs.Ly:=9; Hs.Hoa:=7; Hs.Tin:=10
+ Xem tên, điểm TB của 4 môn trong biến hs
4.2 Vai trò của kiểu tệp
Trang 9+ Kiểu dữ liệu tệp tin giúp chúng ta trao đổi dữ liệu với bộ nhớ ngoài thông qua các tệp tin.
+ Tệp được lưu trữ lâu dài ở bộ nhớ ngoài, lượng dữ liệu lưu trữ trên tệp có thểrất lớn và chỉ phụ thuộc vào dung lượng ổ đĩa
4.3 Phân loại tệp và thao tác với tệp
Các loại tệp tin đã biết: văn bản, chương trình, hình ảnh, âm thanh
Đọc từ đầu đến cuối tệp văn bản
Đọc từng phần của tệp như âm thanh có thể nghe từ nửa bài tệp cấu trúc+ Tệp văn bản (text): dữ liệu ghi vào tệp dưới dạng từng kí tự Đọc và ghi tệp được thực hiện tuần tự, từ đầu đến cuối tệp
+ Tệp có cấu trúc: dữ liệu được lưu trữ theo cấu truc nhất định Đọc và ghi dữ liệu có thể làm trực tiếp đến từng phần tử
4.4 Thao tác với tệp văn bản
Với tệp tin cần làm gì? Đọc dữ liệu đưa ra màn hình, ghi dữ liệu được vào
tệp tin
Các bước thao tác với tệp văn bản:
Bước 1: Khai báo biến tệp
Var <Tên biến tệp> : Text;
Ví dụ: Var Tep1, Tep2: Text;
Đọc: đọc tuần tự bắt đầu từ đầu tệp, nếu tệp chưa có trên đĩa sẽ báo lỗi
Bước 4: Các thao tác đọc và ghi dữ liệu vào tệp
*Đọc dữ liệu từ tệp:
Read(<Tên biến tệp>,<DS biến>);
Trang 10Hoặc Readln(<Tên biến tệp>,<DS biến>);
Lưu ý: DS biến phải cùng kiểu với dữ liệu cần đọc hoặc đọc xâu hay đọc từng
kí tự
Hàm Eof(Biến tệp): có giá trị true khi đã đọc đến cuối tệp
*Ghi dữ liệu vào tệp:
Write(<Tên biến tệp>,<DS biến>);
Hoặc Writeln(<Tên biến tệp>,<DS biến>);
Đóng tệp: thao tác này bắt buộc phải có, nếu không có thể dữ liệu ttrong tệp sẽ
bị mất Khi đã đóng tệp muốn đọc hoặc ghi dữ liệu vào tệp ta cần thực hiện từ B3 (mở tệp)
4.5 Ví dụ
Ví dụ 1: Một file văn bản có tên DaySo.Dat có dạng sau:
- Dòng đầu tiên của số n chỉ số phần tử của dãy số
- Dòng tiếp theo ghi n số, mỗi số cách nhau ít nhất một khoảng trống trên mộtdòng
Viết chương trình đọc dãy số từ file lưu vào trong mảng
READLN(F,n); {doc dong dau tien vao bien n}
For i :=1 to n do readln(f,a[i]); {doc n dong sau vao cac bien a[i]}
Trang 11Ví dụ 2: Một file văn bản ghi số liệu của một bảng số n x m và có dạng sau:
- Dòng đầu tiên của file ghi 2 số n, m cách nhau ít nhất bởi một dấu cách
- N dòng tiếp theo của file ghi số liệu của n hàng, mỗi hang bao gồm n số cáchnhau ít nhất một dấu cách
VD: File so.dat có dạng sau :
II MỘT SỐ THUẬT TOÁN
1 Tính thời gian thực hiện chương trình.
Mục đích: Đánh giá độ phức tạp của giải thuật
a Tính số lần thực hiện:
Uses windows;
Var time: longint;
Trang 12Ta có : x.y = ( x div 2) * 2y + y nếu x lẻ
x.y = ( x div 2) * 2y nếu x chẵn
Function FastMult(x,y: longInt): LongInt;
Trang 133 Phép toán Mod chống tràn số (phép nhân, phép lũy thừa)
Function FastModMult(x,y,n: longInt);
While (x<>0) do Begin
If x mod 2 =1 then z:= z*a mod n; X= x div 2;
a:= a*a mod n;
End;
FastModExp:=z End;
4 Kiểm tra số nguyên dương n có là số nguyên tố hay không
Input: n nguyên dương
Bước 4: Nếu n chia hết cho i thì n không là số NT CB6
Bước 5: i:=i+1 quay lại B3
Bước 6: Kết thúc
Function KTNT(n:LongInt): Boolean;
Var kiemTra: boolean; i: LongInt;
Trang 14kiemTra:= true;
IF n <= 1 then kiemTra:=false
Else
For i:=2 to trunc(sqrt(n) do
If n mod i =0 then Begin kiemTra:=false; Exit End;
KTNT:=kiemTra;
End
Bài tập tương tự: - Sinh số nguyên tố nhỏ nhất sau số nguyên dương n.
- Liệt kê các số nguyên tố từ 2 đến n
- Liệt kê các số nguyên tố trong các số a1, a2 … an.
6 Liệt kê các số nguyên hoàn hảo từ 1 đến n
Procedure SoHoanHao(n: Integer);
Var x,y: Integer;
Trang 15If x mod y =0 then S:=s+i;
Bài toán cần giải quyết các bài toán con:
+ Tìm ước chung lớn nhất của 2 số a, b
+ Tìm số đảo của số x
+ Kiểm tra một số x có là sỗ tam tam không?
+ Liệt kê các số tam tam từ 100 đến 999
Function UCLN(a,b: Integer);
y := 0;
While (x<>0) do Begin y:=y*10 + x mod 10; x:= x div 10;
End SoDao=y;
End Function SoTamTam(x: Integer): Boolean;
For x:= 100 to 999 do
IF SoTamTam(x) Then write(x, ‘ ‘); End
Bài tập tương tự:
1 Số đẹp là số có tận cùng là 36 Đếm số các số đẹp nguyên dương nhỏ hơn n
2 Số nguyên tố hoàn hảo là số nguyên tố và số đảo của nó cũng là số nguyên
tố Đếm số nguyên tố hoàn hảo nhỏ hơn n
3 Một số bài tập xử lý số lớn
- Số trong pascal: single (7 chữ số); longInt( 9 chữ số); real (11 chữ số); double(15 chữ số); extended (19 chữ số), khi dùng extended phải bật chế độ {$N+}
Trang 16- Trong Freepascal int64(19 chữ số)
Nếu số từ 10 chữ số trở lên nên dùng mảng hoặc xâu để lưu trữ
- Cách lưu trữ bằng mảng
Type SNL = array [1…max] of byte;
Bài tập 1: Cho a, b là số nguyên ( 0 a, b, k 1010) cho biết chữ số thứ k sau phầnthập phân của phép a/b
var a,b,k,t,i : extended;
function chia(var a:extended; b:extended):extended;
Trang 17Bài tập tương tự: Cho a, b là số nguyên ( 0 a, b, k 1020) Đưa ra màn hình kết quả phép toán: a mod b, a div b.
Bài tập 2: Nhập n, sinh một số bằng cách viết liên tiếp các số từ 1 đến n Tính tổng
s:=0; i:=1;
while i<=n do begin
s:=s+tongcs(c) ; i:=i+1;
end;
writeln(s:0:0);
readln;
end.
Trang 18Cách 2: Sử dụng xâu làm được với n <= 107, tuy nhiên tốn thời gian chuyển từ xâu sang số và chuyển từ số sang xâu.
s:=0; i:=1;
while i<=n do begin
str(i:10:0,c);
s:=s+tongcs(c) ; i:=i+1;
a[1] =45; a[2] = a[1]*10 +10*45; a[3]= a[2]*10 + 102* 45
+ Tính TongTron(k,n): tổng tất cả các chữ số của số bắt đầu bằng k có n chữ số+ Tính các chữ số được sinh ra đến n= 231
Ví dụ: TinhTong(231) = tongTron(2,3)+ 2* 32 + TinhTong(31)
TinhTong(31)= tongTron(3,2) + 3*3 +TinhTong(1)
while(S<>'') do begin
Trang 19- Cần biết i ở bít thứ mấy: (i-1) mod 8
- Cần biết i ở byte thứ mấy: i div 8 +1
- Để tăng tốc độ tính toán nên sử dụng phép dịch bít
a mod 2n a and (2n-1)
a div 2n a shr n
- Nhận giá trị của bit thứ i của x : if (x and (1 shl i) =0 then gt:=0 else gt =1
- Đặt bit i của x giá trị 0 hoặc 1: Đặt 0: x:= x and(not (1 shl i))
a^[ (i -1) div 8+1]:= a^[(i-1) div 8+1] or (1 shl (7- ((i-1) mod 8))); {bat bit}
for i:=1 to 10 do a^[i]:=255;
i:=2;
Trang 20a^[ (i -1) div 8+1]:= a^[(i-1) div 8+1] and not(1 shl (7- ((i-1) mod 8))); {tat bit}
for i:=1 to 10 do writeln(a^[i]);
Bài tập 3: Một tệp văn bản chứa các số từ 1 đến 1000000, hãy loại bỏ các số trùng
nhau và sắp xếp lại tệp tăng dần
Hướng dẫn:
- Đọc các số lưu vào 1 mảng byte với số phần tử 1000000/8
- Đọc giá trị x đánh dấu mảng tại vị tri bít thứ x bằng 1;
- Ghi mảng vào tệp như sau: Nếu tại bit thứ x bằng 1 thì ghi x
5 Thuật toán sắp xếp nhanh
x=a[r] ;con t=true;
while (r *2<=e) and cont do Begin
j=r*2;
if (j <e ) then
if a[j] < a[j+1] then j=j+1;
if x >= a[j] then cont=false else
Begin a[r]=a[j]; r=j; End; End;
a[r]=x;
End;
Begin for r=(n div 2) downto 1 do sift(r,n); for r=n downto 2 do
Begin Doi_cho(a[1],a[r]); sift(1,r-1); End
Trang 21End;
III TÌM KIẾM LỜI GIẢI
1 Mô hình hóa bài toán
T: Tập các trạng thái của bài toán Các đỉnh đồ thị
F: Tập các toán tử chuyển trạng thái Cạnh, cung của đồ thị
2 Mô tả dữ liệu cho bài toán
Cần lưu trữ T hiệu quả, S,G là tập con của T
F: Thường viết thành 1 hàm xây dựng trạng thái mới từ trạng thái cũ Cần lựa chọn một dạng mô tả nào đó của các trạng thái
T: Thường được sử dụng kiểu tập hợp
Thông thường các giải thuật tìm kiếm đều yêu cầu đánh dấu các trạng thái đã điqua để tránh lặp lại do vậy việc đánh dấu nên dùng BitSet khi không gian trạng tháinhỏ, khi không gian trạng thái quá lớn nên dùng cây tìm kiểm nhị phân để tìm kiếmcho nhanh Nếu cần lưu trữ để lấy kết quả thì dùng mảng Truoc[u] = v để lưu trữtrạng thái trước trạng thái u
Ví dụ:
1 Bảng số: Một bảng số có n dòng m cột mỗi ô lưu một số nguyên dương Tìm cách đi từ ô (x,y) đến ô (x1, y1) theo một cách thức cho trước
+ Mô hình hóa bài toán
Không gian trạng thái lưu trữ bằng mảng 2 chiều
Trạng thái các ô: (x,y)
Trạng thái của ô:
Type Dinh = recordx,y: longint;
End;
Cần mảng lưu trọng số c[u] = c[u.x,u.y] = giá trị tại ô (x,y)