Professional Documents
Culture Documents
Su Dung Free Pascal Day HSG
Su Dung Free Pascal Day HSG
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.
Để đáp ứng được những đòi hỏi trên, 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. Hệ thống lập trình mới như Free
Pascal đã 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.
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 hệ thống lập trình mới hơn là
khá đơn giản.
Ví dụ ngôn ngữ truyền thống PASCAL với hệ thống lập trình Free Pascal.
Đươ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.
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. 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 đề ta cần xem xét và xử lý.
C. BÀI TẬP VÀ CHƯƠNG TRÌNH VIẾT TRÊN HỆ THÔNG FREE PASCAL.
Để hỗ trợ cho việc triển khai giảng dạy trên Pascal (dựa trên cơ sở hệ thống lập trình
Free Pascal). Các bài tập dưới đây sẽ được giới thiệu kèm theo với lời giải. 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.
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. 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.
Sau đây tôi phân chia bài tập cơ bản gồm ba dạng:
+ Các bài tập về số học.
+ Các bài tập về xử lí trên dãy số.
+ Các baaif tập sử dụng xâu.
1. Các bài tập về số học.
Tận dụng hai kiểu số nguyên lớn có sẵn trong hệ thống lập trình Free Pascal là
QWORD và INT64. Sau đây là một số bài tập sử dụng hai kiểu dữ liệu này.
Bài tập 1: Kiểm tra tính nguyên tố của một số nguyên dương N?
Output: đưa ra “Yes” nếu N là nguyên tố hoặc đưa ra “NO” nếu N không là số nguyên
tố.
Đây là một bài toán rất kinh điển khi dạy về ngôn ngữ lập trình. Nếu bài toán này viết
trên hệ thống Turbo Pascal thì là một bài toán khó, vì không có kiểu số nguyên cơ sở
nào có thể lưu trữ được N. Nhưng với bài này, ta sử dụng trên hệ thống Free Pascal thì
rất đơn giản.
- Do đây là bài quá kinh điển nên không cần trình bày dài dòng, sau đây là chương
trình được cài đặt trên hệ thống Free Pascal.
Dựa vào tính chất ước số ta có nhận xét sau: nếu a là ước của n thì ta có n div a cũng
là ước của n.
Ta chỉ cần thử tất cả các số từ 1 đên sqrt(n) là đếm được số ước của n.
Chương trình:
const
Finp='DIVISOR.INP';
Fout='DIVISOR.OUT';
var
n:int64;
s,i:longint;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(n);
s:=0;
for i:=1 to round(sqrt(n)) do
if n mod i=0 then
begin
s:=s+1;
if i<> n div i then
s:=s+1;
end;
write(s);
close(input);
close(output);
end.
Bài tập 3: KHÔNG CHỨA CHÍNH PHƯƠNG
Một số nguyên gọi là không chứa chính phương nếu nó không chia hết cho bất ký số
nguyên nào dạng x2 với x > 1.
Yêu cầu: Cho số nguyên n (1 ≤ n 1013). Hãy tìm ước lớn nhất không chứa chính
phương của n. Lưu ý là 1 và n cũng là ước của n.
Dữ liệu: Vào từ file văn bản SQFREE.INP gồm nhiều tests, mỗi test cho trên một dòng
chứa số nguyên n.
Kết quả: Đưa ra file văn bản SQFREE.OUT, kết quả mỗi test đưa ra trên một dòng.
Ví dụ:
SQFREE.INP SQFREE.OUT
9 3
20 10
- Nếu làm bài toán này theo đúng định nghĩa của bài đưa ra (hay làm theo cách tự
nhiên) thì chỉ qua được những test nhỏ.
- Để giải quyết triệt để bài toán tìm ước không chứa chính phương của N ta phân tích
N thành tích các thừa số nguyên tố, ước lớn nhất thỏa mãn yêu cầu của bài là tích các
thừa số nguyên tố.
Ví dụ N=2i3j5k thì ước lớn nhất không chứa chính phương là 2.3.5
Chương trình:
var
f1,f2:text;
n,i,s:int64;
begin
assign(f1,'sqfree.inp'); reset(f1);
assign(f2,'sqfree.out'); rewrite(f2);
while not eof(f1) do
begin
readln(f1,n);
i:=2;
s:=1;
while n>1 do
begin
if n mod i=0 then
begin
s:=s*i;
while n mod i =0 do
n:=n div i;
end;
i:=i+1;
if i*i>n then break;
end;
if n>1 then s:=s*n;
writeln(f2,s);
end;
close(f1);
close(f2);
end.
Tận dụng khả năng của máy tính hiện nay, bộ nhớ trong kích thước lớn. Hệ thống lập
trình Free Pascal mở rộng bộ nhớ cho chương trình lớn gấp nhiều lần so với Turbo
Pascal (Điều này đã được nói kĩ trong chuyên đề tập huấn đầu năm). Sau đây là một
số bài tập cho thấy việc bộ nhớ chương trình mở rộng giúp cho việc giải một số bài tập
một cách dễ dàng.
Nhiệm vụ của bạn rộng hơn một chút: với n cho trước, hãy xác định số lượng số nguyên
tố p thỏa mãn điều kiện n < p < 2n.
Dữ liệu: Vào từ file văn bản BERTRAN.INP gồm nhiều tests, mỗi test cho trên một
dòng chứa số nguyên dương n (1 ≤ n ≤ 107).
Kết quả: Đưa ra file văn bản BERTRAN.OUT, kết quả mỗi test đưa ra trên một dòng
dưới dạng một số nguyên.
Ví dụ:
BERTRAN.INP BERTRAN.OUT
2 1
239 39
3000 353
Hướng dẫn giải thuật:
+ Với kích thước của bài toán trên, nếu với mỗi số n ta đi thử tất cả các số m từ n+1
tới 2n, kiểm tra m có là số nguyên tố hay không thì chương trình chạy chậm và không
đạt yêu cầu bài.
+ Để làm bài này ta phải sử dụng thuật toán sàng số nguyên tố. Do bộ nhớ Free Pascal
lớn nên ta có thể khai báo mảng a chứa 10 triệu phần tử. Trong đó a[i]=true nếu i là
nguyên tố ngược lại thì i không là nguyên tố.
Chương trình:
const
fi='bertran.inp';
fo='bertran.out';
var
f1,f2:text;
n,res,i,j:longint;
a:array[1..30000000] of boolean;
BEGIN
assign(f1,fi);
reset(f1);
assign(f2,fo);
rewrite(f2);
fillchar(a,sizeof(a),true);
//sang so nguyen to
for i:=2 to round(sqrt(6000000)) do
if a[i] then
for j:=2 to 6000000 div i do
a[i*j]:=false;
while not seekeof(f1) do
begin
readln(f1,n);
res:=0;
for i:=n+1 to 2*n-1 do
if a[i] then
inc(res);
writeln(f2,res,a[2*n-1]);
end;
close(f1);
close(f2);
END.
Bài tập 2: Bộ tộc
Có một hòn đảo rất đẹp, thu hút nhiều khách du lịch đến thăm. Trên đảo có n người
thuộc nhiều bộ tộc sinh sống.Dân cư trên đảo rất thân thiện.Mỗi người thuộc một bộ
tộc nào đó.Trong đoàn du lịch có một nhà nhân chủng học. Tranh thủ dịp may được
ghé thăm đảo ông không bỏ phí thời gian tiến hành khảo sát. Ông gặp từng người một
trên đảo với một câu hỏi duy nhất: “Trên đảo, bộ tộc của bạn có bao nhiêu người?”.
Từ kết quả khảo sát, ông đã xác định được số bộ tộc khác nhau tồn tại trên đảo.
Ví dụ: với n=10 và các câu trả lời là 5, 1, 2, 5, 5, 2, 5, 5, 2, 2 ta có thể suy ra là trên
đảo có 4 bộ tộc khác nhau.
Yêu cầu: cho n và các câu trả lời. Hãy xác định số bộ tộc trên đảo.Dữ liệu đảm bảo
bài toán có nghiệm.
Dữ liệu: vào từ file văn bản CLAN.INP
- Dòng 1 chứa số nguyên n (1<n<3000000)
- Mỗi dòng trong n dòng sau chứa một số nguyên, câu trả lời nhận được.
Kết quả: Đưa ra file văn bản CLAN.OUT một số nguyên, số bộ tộc trên đảo.
Ví dụ:
Clan.inp Clan.out
10 4
5
1
2
5
5
2
5
5
2
2
Hướng dẫn giải thuật
- Đây tiếp tục là một bài thể hiện việc cài đặt trên Free Pascal rất tiện lợi và đơn giản
hơn rất nhiều so với Turbo Pascal.
- Gọi d[i] là số người có câu trả lời là i, số bộ tộc có câu trả lời i là d[i] div i. Ta chỉ
việc đếm số lần xuất hiện của i lưu vào mảng d[i] là giải quyết xong bài toán
Chương trình:
const inp='clan.inp';
out='clan.out';
nmax=3000000+5;
var fi,fo:text;
D:array[0..nmax]of longint;
N,i,j,res:longint;
procedure nhap;
begin
assign(fi,inp);reset(fi);
readln(fi,N);
fillchar(d,sizeof(d),0);
for i:=1 to N do
begin
readln(fi,j);
inc(d[j]);
end;
close(fi);
end;
procedure xuli;
begin
res:=0;
for i:=1 to n do res:=res+d[i] div i;
end;
procedure inkq;
begin
assign(fo,out);rewrite(fo);
writeln(fo,res);
close(fo);
end;
BEGIN
nhap;
xuli;
inkq;
END.
Bài tập 3: HÀNG CÂY
Trong khu vườn, người ta trồng một hàng cây chạy dài gồm có N cây, mỗi cây có độ
cao là a1, a2,…aN.
Người ta cần lấy M mét gỗ bằng cách đặt cưa máy sao cho lưỡi cưa ở độ cao H (mét)
để cưa tất cả các cây có độ cao lớn hơn H (dĩ nhiên những cây có độ cao không lớn
hơn H thì không bị cưa).
Ví dụ: Nếu hàng cây có các cây với độ cao tương ứng là 20; 15; 10 và 18 mét, cần lấy
7 mét gỗ. Lưỡi cưa đặt tại độ cao hợp lí là 15 mét thì độ cao của các cây còn lại sau
khi bị cưa tương ứng là 15; 15; 10 và 15 mét. Tổng số mét gỗ lấy được là 8 mét (dư 1
mét).
Yêu cầu: Hãy tìm vị trí đặt lưỡi cưa hợp lí (số nguyên H lớn nhất) sao cho lấy được M
mét gỗ và số mét gỗ dư ra là ít nhất.
Dữ liệu: Vào từ tệp văn bản WOOD.INP
Dòng thứ nhất chứa 2 số nguyên dương N và M (1≤N≤10 6; 1≤M≤2x109) cách nhau
một dấu cách.
Dòng thứ hai chứa N số nguyên dương ai là độ cao của mỗi cây trong hàng (1≤ai≤109;
i=1…N), mỗi số cách nhau ít nhất một dấu cách.
Kết quả: Đưa ra tệp WOOD.OUT là một số nguyên cho biết giá trị cần tìm.
Ví dụ:
WOOD.INP WOOD.OUT
47 15
20 15 10 18
Hướng dẫn giải thuật:
- Cách 1: Thuật toán vét cạn thử tất cả các độ cao từ cây cao nhất (maxH) tới 0. Với
mỗi độ cao H, tính tổng số mét gỗ thu được, nếu tổng lớn hơn bằng M đầu tiên ta thu
được độ cao cần tìm (đây là cách không đạt điểm tối đa).
- Cách 2: Ta có nhận xét, nếu cắt ở độ cao H lấy được M mét gỗ thì khi cắt độ cao H-
1 cũng lấy được M mét gỗ. Ngược lại, nếu cắt ở độ cao H không lấy được M mét gỗ
thì khi cắt độ cao H+1 cũng không lấy được M mét gỗ. Từ nhận xét trên ta có thể sử
dụng giải thuật tìm kiếm nhị phân để tìm độ cao H cao nhất cần cắt để lấy được M mét
gỗ thỏa mãn yêu cầu bài.
Chương trình:
program laygo;
const
fi='wood.inp';
fo='wood.out';
var
lo,k,hi,mid,s,m:int64;
i,n,H:longint;
f:text;
a:array[0..1000005] of longint;
procedure QSort(l,r:longint);
var x,i,j,tam:longint;
begin
x:=a[l+random(r-l+1)];
i:=l;
j:=r;
repeat
while a[i]>x do inc(i);
while a[j]<x 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 i<r then QSort(i,r);
if l<j then QSort(l,j);
end;
function max(a,b:longint):longint;
begin
if a>b then max:=a
else max:=b;
end;
begin
assign(f,fi);
reset(f);
readln(f,N,M);
lo:=0;
hi:=0;
for i:=1 to n do
begin
read(f,a[i]);
hi:=max(hi,a[i]);
end;
close(f);
{while lo<hi do
begin
mid:=(lo+hi+1) div 2;
s:=0;
for i:=1 to n do
s:=s+max(0,a[i]-mid);
if s<M then hi:=mid-1
else lo:=mid;
end;
H:=lo}
Qsort(1,n);
S:=0;
a[n+1]:=0;
i:=1;
while s+int64(i)*(a[i]-a[i+1])<M do
begin
s:=s+i*(a[i]-a[i+1]);
inc(i);
end;
Chương trình:
const
fi='game.inp';
fo='game.out';
type
Pnode=^TNode;
Tnode=record
pos:longint;
link:Pnode;
end;
var
s,ans1,ans2:ansistring;
list:array['a'..'z'] of Pnode;
dd:array[0..100000+5] of boolean;
ch:char;
i,n,vt:longint;
p:Pnode;
begin
assign(input,fi);
reset(input);
assign(output,fo);
rewrite(output);
readln(n);
readln(s);
for ch:='a' to 'z' do list[ch]:=nil;
for i:=1 to n do
begin
new(p);
p^.pos:=i;
p^.link:=list[s[i]];
list[s[i]]:=p;
dd[i]:=true;
end;
ans1:='';
ans2:='';
vt:=n;
for i:=(n+1) div 2 downto 1 do
begin
//ron di truoc va luon lay o cuoi
while (vt>0) and not dd[vt] do
vt:=vt-1;
if vt>0 then
begin
ans1:=ans1+s[vt];
//xoa ki tu vua
lay
p:=list[s[vt]];
list[s[vt]]:=list[s[vt]]^.link;
dd[p^.pos]:=false;
dispose(p);
end;
//Hermione di sau
for ch:='a' to 'z' do
if list[ch]<>nil then
begin
ans2:=ans2+ch;
p:=list[ch];
list[ch]:=list[ch]^.link;
dd[p^.pos]:=false;
dispose(p);
break;
end;
end;
if ans1>ans2 then writeln('YES')
else writeln('NO');
write(ans2);
close(input);
close(output);
end.
D. Kết luận
Trên đây là một số kinh nghiệm đã được rút ra trong quá trình dạy học sinh giỏi Tin
học trong những năm qua. Các bài tập đã được cài đặt hiệu quả trên hệ thông Free
Pascal.
Kinh nghiệm cho thấy, khi học sinh chuyển từ hệ thống Turbo Pascal sang hệ thống
Free Pascal không gặp khó khăn gì, khi các em đã quen với Free Pascal tỏ ra thích thú.
Đó là một vài kinh nghiệm bản thân muốn chia sẻ với anh chị em đồng nghiệp. Chắc
chắn không tránh được những thiếu xót, rất mong nhận được ý kiến đóng góp của bàn
bè đồng nghiệp để hoàn thiện hơn nữa sáng kiến kinh nghiệm này.
Tôi xin chân thành cám ơn!