ĐỊNH HƢỚNG CHUYÊN MÔN
trong đào tạo – bồi dƣỡng học sinh năng khiếu Tin học
A – ĐỊNH HƢỚNG
Các cuộc thi Olympic Tin học được triển khai rộng rãi ở gần hết các nước trên
thế giới hướng tới các mục đích:
Đẩy mạnh phong trào dạy và học Tin học nhằm đáp ứng các yêu cầu của
cuộc sống đang được tin học hóa sâu rộng và với tốc độ cao trong mọi
lĩnh vực,
Phát hiện các nhân tố nổi bật để đào tạo và khai thác nguồn nhân lực đỉnh
cao, có tri thức và có tay nghề theo kịp sự phát triển của lý thuyết và yêu
cầu của thực tế.
Việc đào tạo, bồi dưỡng học sinh giỏi Tin học chịu tác động rất nhiều của hai
yếu tố:
Sự phát triển của lý thuyết,
Sự phát triển của công cụ Tin học.
Có thể thấy rõ xu hướng dạy và học Tin học cho đến nay chia thành ba giai
đoạn:
Giai đoạn I: những năm cuối của thế kỷ XX,
Giai đoạn II: Thập kỷ đầu tiên của thế kỷ XXI,
Giai đoạn III: Thập kỷ thứ 2 của thế kỷ XXI, tức là những năm hiện tại.
Ở giai đoạn I, ngành Tin học mới tách ra và phát triển thành một ngành khoa
học độc lập. Người ta tập trung mọi sức lực vào việc xây dựng nền móng cho
một ngành khoa học mới, cố gắng tìm cách để giải quyết được các lớp bài toán
truyền thống xuất hiện khi tiếp cận bài toán trên quan điểm toán học. Việc
nắm vững các kiến thức toán học là điều kiện chủ yếu để giải quyết các bài toán
tin học. Ngoài ra còn phải lưu ý tới cấc ràng buộc tự nhiên về bộ nhớ nhỏ và tốc
độ tương đối thấp của máy tính lúc bấy giờ. Đó là sự tiếp tục của tư duy những
năm 70 – 80 của thế kỷ 20.
Ví dụ, bài Hình vuông diệu kỳ (Bài 3 ngày 2 IOI 1996) vào thời điểm đó là một
bài khó.
Hình vuông diệu kỳ
1/64
Những thành công của khối lập phương kỳ diệu đã là động lực để ông Rubic đề
xuất phương án phẳng của trò chơi nổi tiếng này. Đó là một bảng gồm 8 hình
vuông xếp thành 2 hàng (xem
hình vẽ).
7
6
A
8
5
Ph
ép
bi
ến
đổ
i
Phép biến đổi
1 2 3 4
Trong bài này chúng ta xét
B
phương án khi mỗi hình vuông
4 1 2 3
1 2 3 4
Phép biến
được tô một màu khác nhau. Màu
đổi A
Phép biến đổi 5 8
7 6
8 7 6 5
được đánh bằng tám số nguyên
B
Trạng thái ban đầu
Phép biến
dương đầu tiên (xem hình vẽ).
1 7 2 4
đổi C
Cấu hình của bảng được xác định
8 6 3 5
bằng cách cho màu của các hình
vuông, bắt đầu từ hình ở góc trên
trái và đi theo chiều kim đồng hồ. Ví dụ, ở hình bên cấu hình ban đầu của bảng
được xác định bởi dãy (1, 2, 3, 4, 5, 6, 7, 8).
ép
Ph
ến
bi
i
đổ
C
Có 3 phép biến đổi cơ sở ký hiệu là „A‟, „B‟ và „C‟ tác động lên bảng:
„A‟: Hoán đổi vị trí 2 dòng trên và dưới,
„B‟: Đẩy vòng tròn cột cuối lên cột đầu,
„C‟: Đẩy vòng tròn theo chiều kim đồng hồ 4 hình vuông ở giữa.
Cho một cấu hình cuối của bảng. Nhiệm vụ của bạn là viết chương trình xác
định dãy phép biến đổi cơ sở đưa bảng từ trạng thái ban đầu nêu ở hình bên về
trạng thái cuối đã cho (Bài toán con A). Bạn sẽ nhận thêm 2 điểm thưởng nếu số
phép biến đổi trong lời giải của bạn không quá 300.
Dữ liệu: vào từ file INPUT.TXT gồm một dòng chứa 8 số nguyên mô tả trạng
thái cuối của bảng.
Kết quả: Đưa ra file OUTPUT.OUT, dòng đầu tiên chứa số nguyên L – độ dài
của dãy phép biến đổi. Mỗi dòng trong L dòng sau chứa một ký tự - tên phép
biến đổi cần thực hiện.
Công cụ trợ giúp: Chương trình MTOOL.EXE trong thư mục chứa đề bài được
cung cấp để thử nghiệm thực hiện các phép biến đổi và xác định trạng thái cuối
của bảng. Lời gọi chương trình có dạng “MTOOL input.txt
output.txt”.
Ví dụ:
INPUT.TXT
OUTPUT.TXT
2/64
2 6 8 4 5 7 3 1
7
B
C
A
B
C
C
B
Phân tích: Số lượng trạng thái khác nhau của bảng là 8! = 40 320. Việc tổ chức
loang và lưu trữ vết loang từ trạng thái ban đầu tới trạng thái đích trong điều
kiện hiện nay không phải là một chuyện khó. Nhưng vào thời điểm diễn ra kỳ
thi, bộ nhớ có thể được sử dụng chỉ là 64KB + 64KB cấp phát động và máy
thực hiện thuộc loại 386 thì đó là cả một vấn đề lớn.
Chúng ta đã đạt được những kết quả rực rỡ trong thời kỳ này vì đội ngũ giáo
viên tin học phần lớn xuất thân từ giáo viên chuyên ngành toán. Học sinh của
chúng ta được trang bị kiến thức cơ sở về toán khá tốt.
Giai đoạn II từ những năm 2001 đến 2007, sự tiến bộ về công nghệ và sự phổ
cập của các hệ thống phần mềm tiên tiến đã đưa đến những sự chuyển dịch
trong việc đào tạo chuyên gia trong lĩnh vực tin học. Điều này dẫn đến những
thay đổi trong công tác phát hiện, đào tạo và bồi dưỡng học sinh giỏi tin học.
Do đó nội dung thi Tin học Quốc tế cũng có nhiều thay đổi. Các kiến thức giải
thuật được coi là đỉnh cao trước đây bây giờ đã trở thành “bảng cửu chương”
mà ai cũng phải biết và phải thuộc. Những giải thuật phức tạp, ít dùng thì không
nhất thiết phải thuộc, nhưng bất cứ ai và lúc nào cũng có thể tra cứu, tìm kiếm
chúng trên Internet khi cần thiết. Sự thông minh và tính sáng tạo bây giờ phải
thể hiện không phải ở chổ bạn thuộc nhiều hay ít các giải thuật khác nhau, cũng
không phải bạn cài đặt chúng nhanh tới mức nào. Thử thách bây giờ là ở chổ
bạn có thể tìm ra những giải pháp hữu hiệu giải quyết một cách có hiệu quả các
bài toán các vấn đề có mô hình toán học đơn giản nhưng có kích thước lớn hay
không?
Để đạt được mục đích đó người lập trình phải biết tận dụng tối đa khả năng mà
phần cứng và hệ điều hành cung cấp. Các hệ thống lập trình mới như Free
Pascal, Dev C++ (những phiên bản đầu tiên) đã tạo điều kiện để người dùng
khai thác được tối đa khả năng của phần cứng.
Đáng tiếc là tới tận năm 2007 khi thế giới đã đi hết chặng đường quan trọng này
thì chúng ta mới được phép chính thức đặt chân lên mãnh đất đã in đầy dấu
3/64
chân của những người tiên phong, đi vào con đường vốn bây giờ đã trở thành kỷ
niệm đẹp của những người đi trước.
Ở giai đoạn III, tức là ở những năm gần đây, trong danh mục yêu cầu đối với
người lập trình có thêm các đòi hỏi mới:
Không những bạn phải đứng vững trên nền tảng của phần cứng và hệ
thống mà còn phải biết khai thác tối đa khả năng của công cụ lập trình.
Cụ thể, bạn phải khai thác được triệt để mặt mạnh của các thư viện của
hệ thống lập trình có trong tay,
Nâng cao tính hoàn thiện của chương trình: chương trình phải cho kết quả
đúng và xử lý có hiệu quả với từng lớp dữ liệu (của bài toán).
Phải biết tạo một chương trình linh hoạt, kết hợp vài loại giải thuật để giải
một bài toán vốn rất đơn giản nếu kích thước bé.
4/64
Ví dụ, bài Vườn bách thảo (Thi toàn Liên bang Nga 04/2011).
VƢỜN BÁCH THẢO
Trong vườn bách thảo có một khu đất hình chữ nhật kích thước w×h trồng một
loại cây đặc biệt quý hiếm. Với mỗi cây người ta làm một lối đi tạo thành hình
vuông có cây ở tâm. Kích thước hình
vuông phụ thuộc vào vùng bộ rễ của cây y
(w, h)
lan tới. Nếu tạo hệ tọa độ để khu đất này
có 2 đỉnh đối là (0, 0) và (w, h) thì đỉnh
tất cả các hình vuông đều có tọa độ
nguyên. Hai hình vuông bất kỳ không có
phần chung diện tích khác 0, tổng diện
tích các hình vuông có cây đúng bằng
diện tích khu đất, cạnh hình vuông song
song với trục tọa độ.
Thời gian trôi đi và cỏ mọc um tùm che
kín các lối đi. Người ta cần vẽ bản đồ
khôi phục lại các lối đi để lập trình điều
khiển rô bốt chăm sóc cây.
1
1
x
Yêu cầu: Cho w, h, n, xi, yi, trong đó n
là số cây, (xi, yi) – tọa độ cây thứ i (1 ≤ w, h ≤ 1012, 1 ≤ n ≤ 2×105). Hãy xác
định độ dài cạnh hình vuông bao quanh mỗi cây. Dữ liệu đảm bảo tồn tại lời
giải.
Dữ liệu: Vào từ file văn bản GARDEN.INP:
Dòng đầu tiên chứa 3 số nguyên w, h và n,
Dòng thứ i trong n dòng sau chứa 2 số thực xi và yi.
Kết quả: Đưa ra file văn bản GARDEN.OUT n số nguyên trên n dòng, dòng
thứ i chứa cạnh hình vuông có tâm là cây thứ i.
Ví dụ:
GARDEN.INP
4 6 3
1 1
3 1
2 4
GARDEN.OUT
2
2
4
Phân tích: Để đạt được điểm tối đa cần phải biết tổ chức lưu trữ dữ liệu dưới
dạng đồ thị cây, thực hiện các phép xử lý cây, tổ chức tìm kiếm nhị phân trên
trên đó.
5/64
Các hệ thống lập trình như Free Pascal 2.4.4, Dev C++ 4.9.9 đều cung cấp
những thư viện khổng lồ hỗ trợ người dùng và đều có thể mang lại những hiệu
quả tương đương trong lập trình. Tuy vậy C++ nhận được sự lựa chọn đông đảo
thí sinh dự thi hơn vì các lý do:
Thư viện chuẩn hộ trợ lập trình của C++ dễ tiếp cận hơn vì có nhiều tài
liệu giới thiệu,
Có nhiều phiên bản chương trình dịch miễn phí để những Ban tổ chức
cung cấp cho thí sinh,
Ở nhiều nước lấy C++ làm cơ sở giảng dạy ở trường phổ thông,
Trong tương lai, khi lên đại học người ta sẽ dùng C++ là chủ yếu,
Mặt mạnh của Pascal là khả năng thể hiện có cấu trúc trong quá trình
triển khai giải thuật không còn là trọng tâm trong nội dung giảng dạy,
C++ cho phép xây dựng chương trình tối ưu, thể hiện các tiểu xảo lập
trình.
Tuy vậy, C++ cũng có những mặt yếu của nó so với Pascal. Ta sẽ nói đến
những vấn đề này muộn hơn, khi đề cập tới công cụ lập trình.
B – CÔNG CỤ LẬP TRÌNH
Trang bị kiến thức về ngôn ngữ lập trình chưa bao giờ là một vấn đề lớn trong
tin học. Điều quan trọng là kỹ thuật lập trình và tổ chức dữ liệu. Khi đã biết
tương đối tốt một ngôn ngữ lập trình thì việc chuyển sang lập trình ở một ngôn
ngữ mới khá đơn giản.
Bên cạnh ngôn ngữ truyền thống PASCAL với hệ thống lập trình Free Pascal và
C++ với hệ thống lập trình DEV C++, nhiều nước còn cho phép và khuyến
khích sử dụng một loạt các ngôn ngữ khác như Delphi, Python, Java, C#, v . v .
..
Đương nhiên, khi chọn một ngôn ngữ nào đó làm công cụ cho mình người lập
trình cần phải:
Biết rõ những điểm mạnh và yếu của ngôn ngữ cũng như của hệ thống hệ
thống lập trình hỗ trợ,
Cần nắm vững các dịch vụ mà hệ thống lập trình cung cấp,
Cần có thói quen suy nghĩ và hành động phù hợp với ngôn ngữ và hệ
thống lập trình,
Cần biết càng sâu càng tốt các thư viện chuẩn hỗ trợ lập trình và biết
khai thác chúng một cách tối ưu.
6/64
Nếu có cách tiếp cận hợp lý thì những vấn đề trên có thể giải quyết được một
cách khá đơn giản và hiệu quả.
Ở nước ta, trong bậc PTTH hệ thống lập trình được sử dụng phổ biến là ngôn
ngữ PASCAL với hệ thống lập trình Free Pascal. Ở bậc đại học, ngôn ngữ lập
trình chủ yếu là C/C++ với hệ thống Dev C++.
Trên thế giới nhiều nước sử dụng C++ với các hệ thống lập trình Dev C++ hoặc
tương đươngngay từ bậc phổ thông trung học.
Việc giảng dạy đại trà trong nhà trường không phải là vấn đề thảo luận ở đây.
Nhưng việc trang bị công cụ cho học sinh năng khiếu, phục vụ cho các kỳ thi
Tin học là vấn đề nằm trong tầm xem xét và xử lý của chúng ta.
Các thành phần và cấu trúc tƣơng đƣơng của 2 hệ thống lập trình
Các phép tính số học
Stt
Ví dụ
PASCAL C/C++
+
*
/
DIV
MOD
+
*
/
/
%
Pascal
a
a
a
a
n
n
+ b
- b
* b
/ b
div m
mod m
C/C++
a
a
a
a
n
n
+
*
/
/
%
b
b
b
b
m
m
Các phép tính quan hệ
Stt
PASCAL C/C++
<
<=
=
>=
<>
and
or
not
<
<=
==
>=
!=
&&
||
!
Ví dụ
Pascal
a < b
a <= b
a = b
a >= b
a<>b
Pascal sử dụng các
phép xử lý bit để tạo
biểu thức quan hệ.
7/64
C/C++
a < b
a <= b
a == b
a >= b
a!=b
Các phép tính xử lý bit
Stt
PASCAL C/C++
not
and
or
xor
shl
shr
Ví dụ
Pascal
C/C++
~
|
&
^
<<
>>
Khai báo
PASCAL
C++
Var i,j,k:integer;
m,n: longint;
a,b:real;
p,d:int64;
x:array[0..20] of longint;
y:array[1..10, 1..5] of longint;
z:array[1..15] of real;
c:char;
s:string;
int i,j,k;
long m,n,x[21], y[10][5];
float a,b,z[15];
long long p,d;
char c;
string s;
Lệnh gán
C++
PASCAL
a:=y[i,j];
a:=a+b;
a:=a*z[i];
i:=i+1;
k:=i div j;
k:=i mod j;
a=y[i][j];
a+=b;
// a=a+b;
a*=z[i];
i++;
// ++i;
k=i/j;
k=i%j;
8/64
Lệnh IF
Không có then
PASCAL
if a<> b then c=c+5;
if a= b then z[i]:=0 else
begin
z[i]:= z[i]+c;
z[n-i]:=z[n-i]-c
end;
Đặt điều kiện
trong ngoặc
Lưu ý có ;
C++
if (a!=b) c+=5;
if (a==b)z[i]=0;
else
{z[i]+=c;
Z[n-i]-=c;
}
Lưu ý có ;
Câu lệnh FOR
C++
PASCAL
for i:=1 to n do
begin
. . . .
end;
for j:= n downto 1 do
begin
. . . .
end;
for (i=1;i<=n;i++)
{
. . . .
}
for (i=n;i>0;i--)
{
. . . .
}
Lƣu ý: Có thể khai báo biến i cục bộ hóa trong chu trình:
C++
for (int i=1;i<=n;i++){ . . . }
for (int i=n;i>0;i--) { . . . }
9/64
Tổ chức chu trình có số lần lặp không biết trƣớc
C++
PASCAL
repeat
. . . . .
until j=i;
do
{
. . . .
} while (i==j);
while i
begin
. . . .
end;
while (i
{
. . . .
}
Phần đầu chƣơng trình
Ở PASCAL có thể có hoặc không có phần đầu chương trình:
PASCAL
Khai báo thư viện
Program NIM;
Uses crt, matrix;
Khai báo thư viện,
thừa không sao!
Ở C++: bắt buộc phải khai báo các thư viện:
C++
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
10/64
Tổ chức vào ra với file văn bản
Trong phần khai báo.
Flies tự động được mở,
C++
PASCAL
ifstream fi (“NIM.INP”);
ofstream fo (“NIM.OUT”);
Const tfi=‟NIM.INP‟;
Tfo=‟NIM.OUT‟;
Var fi,fo:text;
. . . . . . . .
fi>>a>>b>>c;
fi.close();
. . . . . . . .
fo<
fo<
fo.close();
. . . . . . .
Assign(fi,tfi); reset(fi);
Readln(fi,a,b,c);
Close(fi);
. . . . . . .
Assign(fo,tfo);
Rewrite(fo);
Writeln(fo,a,‟ „,b);
Write(fo,c);
Close(fo);
Chƣơng trình con và chƣơng trình chính
PASCAL
Procedure p1(i,j:integer);
Begin . . .
End;
Procedure p2;
Begin . . .
End;
Function fact(i:integer);integer;
Begin
. . . . . .
fact:=k
End;
. . . . . .
BEGIN
. . . .
END.
11/64
C++
void p1(int i,int j)
{ . . . . }
void p2()
{ . . . . }
int fact(int i)
{ . . .
return(k);
}
. . . . . . .
int main()
{ . . . .
}
Ví dụ:
C++
PASCAL
Program NIM;
Const tfi='NIM.INP';
tfo='NIM.OUT';
Var a:array[1..1000] of longint;
i,n,g,t,k:longint;
fi,fo:text;
BEGIN
assign(fi,tfi);
reset(fi);
readln(fi,n);
for i := 1 to n do readln(fi,a[i]);
close(fi);
g:=0;
for i:=1 to n do g:=g xor a[i];
k:=0;
for i:=1 to n do
begin
t:=a[i]-(a[i] xor g);
if t>0 then
begin
inc(k);
a[i]:=t
end
else a[i]:=0
end;
assign(fo,tfo); rewrite(fo);
writeln(fo,k);
Xử lýifxâu
k > 0 then
for i:=1 to n do if a[i]>0 then
writeln(fo,i,' ',a[i]);
close(fo)
END.
12/64
#include <fstream>
#include <conio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{long n,g,t,k;
ifstream fi ("NIM.INP");
ofstream fo ("NIM.OUT");
fi >> n;
{long a[n+1];
for (long i = 1; i <=n;i++)
fi>>a[i];
k=0;
g=0;
for (long i=1; i <=n;i++) g^=a[i];
if (g>0)
{ for (long i=1;i<=n;i++)
{ t = a[i] – (a[i]^g);
if (t>0)
{k++;a[i]=t;
} else a[i]=0;
}
}
fo<
if (k>0)
for (long i= 1; i<=n;i++) if
(a[i]>0) fo<
fi.close(); fo.close();
}
}
Xâu là một trong các loại dữ liệu cơ bản mà các hệ thống lập trình đều phải
cung cấp địch vụ để khai báo, lưu trữ và xử lý.
Cả trong PASCAL và C++ đều có 2 loại xâu:
Trong PASCAL:
o Xâu kiểu string (ngắn và dài),
o Xâu kiểu Pchar trong hệ thống lập trình Free Pascal,
Trong C++:
o Xâu dạng C,
o Xâu dạng C++.
Tồn tại một loạt các dịch vụ cung cấp các phép xử lý xâu loại 2 và chuyển đổi
dạng biểu diễn xâu. Các dịch vụ này được tổ chức trong thư viện Strings của
PASCAL và string của C++.
string s(“ABCD12345abcdABCDE2”);
string ns(“0123456789”);
l=s.size();
k=s.find(“BC”);
m=s.rfind(“BC”);
p=s.find_first_of(„C‟);
q=s.find_last_of(„C‟);
i=s.find_first_of(ns);
j=s.find_last_not_of(ns);
// l=s.length(); 19 l
// 1 k (bắt đầu từ 0)
// 14 m
// 2 p
// 15 q
// 4 i
// 17 j
13/64
C++
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{ string
s=("ABCD12345abcdABCDE2");
string ns=("0123456789");
int l,k,m,p,q,i,j;
ofstream fo ("xl_xau.out");
l=s.size();
k=s.find("BC");
m=s.rfind("BC");
p=s.find_first_of('C');
q=s.find_last_of('C');
i=s.find_first_of(ns);
j=s.find_last_not_of(ns);
XL_XAU.OUT
L
K
M
P
Q
I
J
fo<<"L = "<
fo<<"K = "<
fo<<"M = "<
fo<<"P = "<
fo<<"Q = "<
fo<<"I = "<
fo<<"J = "<
fo.close();
}
14/64
=
=
=
=
=
=
=
19
1
14
2
15
4
17
PASCAL cũng cung cấp những dịch vụ tương tự trong thư viện Strings. Dưới
đây là vài ví dụ nêu trong tài liệu hướng dẫn sử dụng của Free Pascal.
PASCAL
Program Example13 ;
Uses s t r i n g s ;
{ Program to demonstrate the StrScan and StrRScan f u n c t i o n s . }
Const P : PChar = ’ This i s a PCHAR s t r i n g . ’ ;
S : Char = ’ s ’ ;
begin
Writeln ( ’P , s t a r t i n g from f i r s t ’ ’ s ’ ’ : ’ ,StrScan (P, s ) ) ;
Writeln ( ’P , s t a r t i n g from l a s t ’ ’ s ’ ’ : ’ ,StrRScan (P, s ) ) ;
end .
Find_first và Find_last
PASCAL
Program Example14 ;
Uses s t r i n g s ;
{ Program to demonstrate the StrLower and StrUpper f u n c t i o n s . }
Const P1 : PChar = ’THIS IS AN UPPERCASE PCHAR STRING ’ ;
P2 : PChar = ’ t h i s i s a lowercase s t r i n g ’ ;
begin
Writeln ( ’ Uppercase : ’ ,StrUpper (P2 ) ) ;
StrLower ( P1 ) ;
Writeln ( ’ Lowercase : ’ ,P1 ) ;
end .
Chữ hoa và chữ thường
(có công cụ tương đương trong C)
15/64
PASCAL
Program Example15 ;
Uses s t r i n g s ;
{ Program to demonstrate the StrPos f u n c t i o n . }
Const P : PChar = ’ This i s a PChar s t r i n g . ’ ;
S : Pchar = ’ i s ’ ;
begin
Writeln ( ’ Pos i t ion of ’ ’ i s ’ ’ i n P : ’ , s i z e i n t ( StrPos (P,S)) - s i z e i n t (P ) ) ;
end .
Tìm vị trí xâu con
Như vậy, việc dùng PASCAL hay C++ đều có thể giải quyết một cách hiệu quả
các bài toán thi học sinh giỏi. Tuy vậy, trong mọi trường hợp, học sinh cần
được:
Trang bị thêm các kiến thức mới,
Có thói quen khai thác các công cụ trong hệ thống lập trình,
Biết cách sử dụng hợp lý và có hiệu quả các công cụ hiện có.
Đã là một công dạy và học, tại sao ta không làm việc ngay với C++? Điều này
là hoàn toàn khả thi và có lợi vì:
Về mặt pháp lý:
o Bộ Giáo dục cho phép sử dụng C/C++ trong kỳ thi,
o Công cụ: cũng thuộc loại Open Sources (miễn phí),
Về thời gian:
o 02 tiết cho việc giới thiệu cách viết chương trình trên C++,
o 02 tiết cho việc làm quen với môi trường lập trình Dev C++,
o Các kiến thức khác, nếu cần, trang bị dần trong suốt quá trình bồi
dưỡng (theo nguyên tắc “Mưa dầm thấm đất”),
Lợi ích:
o Thư viện chuẩn STL của C++ có nhiều dịch vụ cần thiết với học
sinh (ví dụ như các công cụ sắp xếp theo các giải thuật khác nhau,
các công cụ tìm kiếm, tổ chức stack, heap, xử lý vector, ma trận v.
v. . .),
o Tài liệu: rất phong phú,
16/64
Về mục tiêu chiến lược lâu dài:
o Các kiến thức về kỹ năng lập trình sẽ được tận dụng triệt để trong
tương lai khi học sinh lên đại học và ra làm việc,
o Đáp ứng yêu cầu về chiến lược đào tạo và nâng cao chất lượng
Giáo dục của nhà nước.
Tất nhiên PASCAL với hệ thống lập trình Free Pascal vẫn được sử dụng song
song như một tùy chọn cho những người yêu thích và vẫn hoàn toàn có thể đạt
kết quả cao trong các kỳ thi.
Các vấn đề cần lƣu ý khi chuyển đổi công cụ
Trong thời gian đầu cần trợ giúp học sinh các vấn đề:
Các loại lỗi cú pháp thường gặp ở giai đoạn đầu,
Cách tạo file dữ liệu,
Kỹ thuật cục bộ hóa biến,
Khai thác giao diện của Dev C++ và các công cụ hiệu chỉnh,
Vấn đề biến đổi kiểu dữ liệu trong biểu thức,
Giới thiệu về sự tồn tại và tác dụng của một số thư viện trong STL.
Tránh trang bị dồn dập kiến thức mới, đặc biệt là về các thư viện. Phong cách
lập trình theo kiểu C++ sẽ tự động được hình thành dần theo thời gian, đúng với
tinh thần “Trăng đến rằm sẽ tròn”.
Các vấn đề này sẽ được xem xét ở các phần tiếp theo.
17/64
SẮP XẾP
Hàm sắp xếp nhanh trên C++
void quickSort(int arr[], int left, int right) {
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
/* partition */
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
1
12
5
26
7
14
3
7
2
Chưa sắp xếp
1
12
5
26
7
14
3
7
2
Giá trị trục = 7
1
12
5
26
7
14
3
7
2
12 ≥ 7 ≥ 2 đổi chổ
1
i
2
5
26
7
14
3
7
1
2
5
7
14
i
Giá trị trục
j
j
12
26 ≥ 7 ≥ 7 đổi chổ
3
26 12
7 ≥ 7 ≥ 3 đổi chổ
7
26 12
i > j - kết thúc xử lý nhóm
7
26
j
i
7
i
1
/* recursion */
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
2
5
7
j
3
14
j
i
1 2 5 7 3
14
12
Xử lý đệ quy các nhóm
1
2
3
5
7
7
12 14 26
Trong nhiều trường hợp thường phải sắp xếp đồng thời 2 dãy Chỉ số CS và Giá
trị B, tức là sắp xếp cặp (csi, bi) theo giá trị tăng dần của bi và với những bi
bằng nhau cặp (csi, bi) có csi nhỏ hơn sẽ đứng trước.
SẮP XẾP 2 DÃY
Ta có thể dễ dàng bổ sung mở rộng hàm sắp xếp nêu trên để giải quyết vấn đề
này. Tuy vậy, để tận dụng tối đa các khả năng sắp xếp khác nhau do hệ thống
lập trình cung cấp ta cần tổ chức dữ liệu theo kiểu véc tơ, heap. . . . Chương
trình sắp xếp sẽ có dạng:
Chƣơng trình trên C chuẩn:
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
typedef struct
18/64
{
int val, idx; //val: b[i], idx: cs[i]
}Element;
typedef Element Arr[MAX];
int cmpf(const void *a, const void *b);
int main()
{
Arr a = {{5, 4}, {3, 2}, {5, 3}, {5, 1}, {2, 5}};
int n = 5;
qsort(a, n, sizeof(Element), cmpf);
for (int i=0; i
printf("%d %d\n", a[i].val, a[i].idx);
getchar();
}
int cmpf(const void *a, const void *b)
{
return ((((Element *)a)->val - ((Element *)b)->val>0)||
(((Element *)a)->val == ((Element *)b)->val)&&(((Element
*)a)->idx > ((Element *)b)->idx));
}
Chƣơng trình trên C++:
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
typedef class CElement
{
int val, idx; //val: b[i], idx: cs[i]
public:
CElement(int v=1, int i=1)
{
val = v; idx = i;
}
int GetValue()
{
return val;
}
};
int cmpf(const void *a, const void *b);
19/64
typedef class CArr
{
int n;
CElement data[MAX];
public:
CArr()
{
}
void Sort()
{
qsort(data, n, sizeof(CElement), cmpf);
}
};
int main()
{
CArr a;
//Nhap du lieu cho a
//....
a.Sort();
//...
//Xuat ket qua cua a
system("PAUSE");
return 0;
}
int cmpf(const void *a, const void *b)
{
return (((CElement *)a)->GetValue() - ((CElement *)b)>GetValue());
}
Ghi chú: Nội dung của phần sắp xếp và sắp xếp 2 dãy được biên soạn theo tài liệu của thầy
Nguyễn Thanh Sơn, trường Phổ thông Năng khiếu , Đại học Quốc gia Thành phố Hồ Chí
Minh.
20/64
BÀI TẬP VÀ CHƢƠNG TRÌNH
Để hỗ trợ cho việc triển khai giảng dạy trên C++ các bài tập dưới đây sẽ được giới thiệu kèm
theo với lời giải trên ngôn ngữ C/C++ và Pascal (dựa trên cơ sở hệ thống lập trình Free
Pascal). Khi đã có đủ tự tin trong việc lập trình ta sẽ thấy lời giải đưa ra dưới dạng chương
trình hoàn thiện chỉ có tác dụng chủ yếu để tạo tests.
Việc trình bày giải thuật dưới dạng sơ đồ khối cũng không giúp nhiều cho việc nắm bắt giải
thuật mà chỉ hỗ trợ cho việc viết chương trình (coding) được dễ dàng. Ngoài ra, sơ đồ khối
còn hạn chế tính sáng tạo trong việc triển khai giải thuật.
Với mỗi bài toán, quan trọng và cần thiết hơn cả là gợi ý về giải thuật, bao gồm:
Hướng triển khai giải thuật,
Cần có những dữ liệu gì, tổ chức như thế nào, giá trị đầu, cách biến đổi, dẫn xuất,
Những điểm đặc biệt cần lưu ý,
Đánh giá độ phức tạp,
Với một vài khâu xử lý quan trọng: có thể dẫn xuất câu lệnh hoặc đoạn chương trình.
Việc dẫn xuất đầy đủ chương trình cũng rất cần thiết, nhưng chỉ ở giai đoạn đầu của quá trình
đào tạo, bồi dưỡng học sinh.
Việc tổ chức gợi ý giải thuật còn mất nhiều thời gian và công sức hơn cả việc trực tiếp viết
chương trình giải!
Tuyệt đối tránh cung cấp cho học sinh chương trình hoàn thiện ngay từ đầu, khi học sinh
chưa hiểu giải thuật, chưa bắt tay tự mình lập trình.
Lời giải cho các bài tập ở phần đầu được đưa ra ở dạng song ngữ. Ở nửa cuối của phần bài
tập, khi đã có đủ kinh nghiệm với C++, các lời giải chỉ đưa ra ở dạng chương trình trên C++
hoặc C/C++.
21/64
Tên chương trình: SUBSETS.???
TẬP CON
Cho 2 tập số nguyên X={x1, x2, . . ., xm} và Y={y1, y2, . . ., yn}, trong đó:
x1 < x2 < . . . < xm,
y1 < y2 < . . . < yn.
Nói X có thứ tự từ điển nhỏ hơn Y nếu thỏa mãn một trong hai điều kiện:
i sao cho x1 = y1, x2 = y2 , . . ., xi-1 = yi-1, xi < yi,
m < n và x1 = y1, x2 = y2 , . . ., xm = ym.
Xét tập số nguyên {1, 2, . . ., n}. Từ tập này người ta có thể trích ra các tập con, trong mỗi tập
con các phần tử được liệt kê theo thứ tự tăng dần của giá trị và sắp xếp các tập con nhận được
theo thứ tự tự điển tăng dần. Các tập con được đánh số bắt đầu từ 1.
Ví dụ, với n = 3 ta có các tập con:
1: { 1 }
2: { 1, 2}
3: { 1, 2, 3}
4: { 1, 3 }
5: { 2 }
6: { 2, 3}
7: { 3 }
Yêu cầu: Cho hai số nguyên n và k (1 < n ≤ 60, 1 ≤ k ≤ 260-1). Hãy xác định tập con thứ k.
Dữ liệu: Vào từ file văn bản SUBSETS.INP gồm nhiều tets, mỗi test cho trên một dòng chứa
2 số nguyên n và k.
Kết quả: Đưa ra file văn bản SUBSETS.OUT, kết quả mỗi test đưa ra trên một dòng chứa các
số nguyên xác định tập tìm được.
Ví dụ:
SUBSETS.INP
SUBSETS.OUT
3 2
4 9
1 2
2
22/64
k--
i =n-1 ¸ 0
k k-1
1 0 1
2 1 1 2
3 2 1 2 3
4 3 1 3
5 4 2
6 5 2 3
7 6 3
i =n-1 ¸ 0
Bit thứ i của
k t
F
t=0
T
F
di = 1
T
di=0
di=1
k=0
Đưa ra i
T
F
k--
C++
#include <fstream>
#include <string>
int n,d[61];
long long k,t,t1=1;;
using namespace std;
ifstream fi ("SUBSETS.INP");
ofstream fo ("SUBSETS.OUT");
void xly()
{ k--;
for (int i=n-1; i>=0;i--)
{ t=(t1<
if ( t==0)
{d[n-i]=1; if (k==0) break;k--;}
else d[n-i]=0;
}
for (int i=1;i<=n;i++) if (d[i]==1) fo<
fo<
}
int main()
{ while (! fi.eof())
{ fi>>n>>k;
//if (n==0) break;
xly();
}
fi.close();
fo.close();
}
23/64
Exit
PASCAL
Program SUBSETS;
Const tfi='SUBSETS.INP';
tfo='SUBSETS.OUT';
t1:int64=1;
Var n:integer;
k,t:int64;
d:array[1..60] of byte;
fi,fo:text;
Procedure xly;
var i:integer;
Begin
dec(k);
for i:= n-1 downto 0 do
begin
t:=(t1 shl i) and k;
if t=0 then
begin d[n-i]:=1;
if k = 0 then break; dec(k)
end
else begin d[n-i]:=0; end
end;
for i:=1 to n do if d[i]=1 then write(fo,i,' ');
writeln(fo)
End;
BEGIN
assign(fi,tfi); reset(fi);
assign(fo,tfo); rewrite(fo);
while not eof(fi) do
begin
readln(fi, n, k);
xly
end;
close(fi); close(fo)
END.
24/64
Tên chương trình: INTERNET.???
INTERNET
Giá truy cập internet phụ thuộc vào từng thời điểm trong tuần. Các ngày trong tuần được
đánh số từ 1 đến 7 bắt đầu từ thứ 2. Bảng tính giá gồm n bản ghi (1 ≤ n ≤ 100), sắp xếp theo
thứ tự tăng dần của thời gian, mỗi bản ghi có dạng:
d cc:mm v
trong đó d là ngày trong tuần, cc – 2 số chỉ giờ (chế độ 24 giờ), mm – 2 số chỉ phút, v – giá
mỗi phút truy nhập, là số nguyên (1 ≤ v ≤ 104). Giá truy nhập được giữ nguyên cho đến khi
gặp thời điểm mới ở bản ghi tiếp theo trong bảng. Sau bản ghi thứ n là bảng ghi số 1.
Yêu cầu: Cho bảng tính giá và danh sách xác định m phiên truy nhập mạng (1 ≤ m ≤ 105).
Mỗi phần tử của danh sách có dạng d cc:mm t, trong đó d, cc, mm có ý nghĩa như trong
bảng giá, t – thời gian truy nhập tính theo phút, là số nguyên (1 ≤ t ≤ 109). Các phiên truy
nhập có thể thuộc những tuần khác nhau. Hãy tính chi phí “lướt” mạng.
Dữ liệu: Vào từ file văn bản INTERNET.INP:
Dòng đầu tiên chứa 2 số nguyên n và m,
Mỗi dòng trong n dòng tiếp theo chứa một bảng ghi của bảng giá,
Ở m dòng cuối cùng mỗi dòng chứa một bản ghi về một phiên truy nhập.
Kết quả: Đưa ra file văn bản INTERNET.OUT một số nguyên – chi phí phải trả.
Ví dụ:
INTERNET.INP
2 3
1 09:00 500
5 22:00 200
2 22:42 16
5 21:06 57
2 22:50 1
INTERNET.OUT
36100
25/64