SlideShare a Scribd company logo
1 of 49
Năm học 2002-2003
Bài 1: CHỮ SỐ tên file chương trình CHUSO.PAS
Xét dãy số tự nhiên {an} đuợc xây dựng theo quy tắc sau:
• Cho trước số a0 là một số tự nhiên có tối đa 10 chữ số.
• Số ai (i>0) là một số tự nhiên nhận được từ ai-1 bằng cách viết thêm vào sau
các chữ số của ai-1 chính ai-1 nhưng viết theo thứ tự ngược lại.
Ví dụ:
Với a0 = 123 thì a1 = 123321, a2 = 123321123321, a3 = 123321123321123321123321
Với hai số N và M cho trước, hãy tìm chữ số thứ M trong aN.
Dữ liệu cho trong file văn bản với tên là CHUSO.INP trong đó dòng đầu chứa số a0,
dòng thứ hai chứa hai số N và M.
Kết quả ghi ra file văn bản với tên là CHUSO.OUT. Trong trường hợp có lời giải, file
này sẽ chứa số tìm được, ngược lại file này chứa số -1.
Ví dụ:
CHUSO.INP
123
3 7
Giới hạn: 1 ≤ N ≤ 25, 1 ≤ M ≤ 1 000 000 000.
Lời giải:
Trước tiên ta nhận xét mặc dù đề bài cho a0 là số tự nhiên, nhưng vì bài toán không
sử dụng tính chất của số nên ta có thể xem a0 như một xâu ký tự.
Gọi L là số ký tự của a0, ta thấy a0 có L ký tự, a1 có 2L ký tự, a2 có 4L ký tự, ..., aN có
2N
L ký tự.
Ký hiệu sR
là chuỗi ký tự đảo ngược của chuỗi s. Ví dụ: nếu s="abca" thì sR
="acba",
các chữ cái được viết theo thứ tự ngược lại. Để ý là với hai chuỗi a, b bất kỳ ta có
(ab)R
=bR
aR
. Theo đề bài, ta có a1 = a0a0
R
, a2=a1a1
R
=a0a0
R
(a0a0
R
)R
=a0a0
R
a0a0
R
, ..., aN=
a0a0
R
a0a0
R
... a0a0
R
, nghĩa là xâu aN sẽ ghép thành từ các xâu a0 và a0
R
xen kẽ nhau.
Từ nhận xét này ta có thuật toán sau:
Nếu vị trí M nằm ngoài xâu ký tự aN, hay nói cách khác nếu M < 1 hoặc M > 2N
L thì in
ra -1. Đoạn lệnh sau đây thể hiện điều này:
if (m<1) or (m>l*(1 shl n)) then begin {chú ý: 1 shl n = 2^n}
timchuso:=-1;
exit;
end;
Còn nếu không, ta xem vị trí M sẽ thuộc về một xâu a0 hay một xâu đảo ngược a0
R
,
điều này được chỉ ra bởi giá trị của biểu thức ((M-1) div L) mod 2 bằng 0 hay 1. Biết
được điều này, sử dụng phép lấy số dư, ta sẽ tìm được vị trí của ký tự cần tìm trong
chuỗi a0:
i:=(m-1) mod l+1; {i là vị trí tương ứng với vị trí M trong chuỗi a0}
if ((m-1) div l) mod 2=1 then i:=l-i+1; {nếu vị trí M thuộc xâu đảo ngược a0
R
thì
ta đảo ngược vị trí i}
timchuso:=ord(a0[i])-ord('0'); {kết quả bằng chữ số a0[i]}
1
CHUSO.OUT
1
Chương trình:
const
finp='chuso.inp';
fout='chuso.out';
var
a0: string;
n, m: longint;
function timchuso(n,m: longint): longint;
var l, i: longint;
begin
l:=length(a0);
if (m<1) or (m>l*(1 shl n)) then begin timchuso:=-1; exit; end;
i:=(m-1) mod l+1;
if ((m-1) div l) mod 2=1 then i:=l-i+1;
timchuso:=ord(a0[i])-ord('0');
end;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
readln(a0);
readln(n,m);
write(timchuso(n,m));
close(input);
close(output);
end.
Bài 2: TÍNH DIỆN TÍCH tên file chương trình HCN.PAS
Trên mặt phẳng tọa độ cho N (N ≤ 10 000) hình chữ nhật với các cạnh song song
với các trục tọa độ. Các hình chữ nhật được đánh số từ 1 tới N. Hình chữ nhật thứ i
được cho bởi toạ độ đỉnh trái dưới (xi1 , yi1) và tọa độ đỉnh phải trên (xi2, yi2). Các số xi1
, yi1, xi2, yi2 là các số nguyên trong phạm vi từ -100 đến 100.
Hãy lập trình tính:
1. Diện tích của phần mặt phẳng mà N hình chữ nhật này phủ.
2. Tính diện tích phần chung của N hình chữ nhật này.
Dữ liệu cho trong file HCN.INP trong đó dòng đầu chứa số N. Dòng thứ i trong N
dòng tiếp theo chứa 4 số số xi1 , yi1, xi2, yi2.
Kết quả ghi ra file HCN.OUT gồm 2 dòng, trong đó dòng đầu chứa số S1 là kết quả
của câu 1. Dòng thứ hai chứa số S2 là kết quả của câu 2.
2
Ví dụ:
HCN.INP
2
0 0 1 1
-1 -1 1 1
3
HCN.OUT
4
1
Bài 3: BẢNG QUẢNG CÁO Tên file chương trình QUANGCAO.PAS
Trên quảng trường trung tâm của thủ đô Rome có một bảng quảng cáo hình
chữ nhật gồm N x M ô vuông. Mỗi ô có một bóng đèn, mỗi bóng đèn có hai trạng thái
tắt hoặc sáng. Ứng với mỗi dòng cũng như mỗi cột có một công tắc. Khi tác động
đến một công tắc nào đó tất cả các bóng đèn trên dòng hoặc cột tương ứng sẽ đổi
sang trạng thái ngược lại (đang sáng thành tắc, đang tắc được bật sáng). Để mừng
đội nhà thắng trận trong trận cầu chiều qua người phụ trách bảng quảng cáo muốn
bảng có được nhiều bóng đèn sáng nhất.Với trạng thái bảng quảng cáo hiện thời
cho trước, người phụ trách nhờ bạn lập trình tìm một phương án tác động lên các
công tắc để nhận được trạng thái bảng quảng cáo mong muốn. Bạn hãy giúp nhà
phụ trách thực hiện điều đó.
Dữ liệu cho trong file văn bản với tên là QUANGCAO.INP trong đó:
• Dòng đầu chứa hai số N và M (: 1 ≤ N ≤ 10, 1 ≤ M ≤ 100).
• Dòng thứ i trong N dòng tiếp theo chứa M số 0 hoặc 1. Số thứ j cho biết trạng
thái của bóng đèn thứ j trên dòng thứ i của bảng (1 tương ứng với bóng đèn
sáng, 0 tương ứng với bóng đèn tắt).
Kết quả ghi ra trong file QUANGCAO.OUT trong đó:
• Dòng đầu là số bóng đèn sáng trên bảng tìm được
• Dòng thứ hai chứa S là số lần bạn tác động lên các công tắc.
• S dòng tiếp theo lần lượt ghi ra S công tắc theo trình tự cần bật. Dòng thứ j
trong S dòng này chứa một xâu độ dài không quá 4, ký tự đầu là ‘D’ hoặc ‘C’
tương ứng với tác động thứ i là lên dòng hay cột. Phần còn lại của xâu là chỉ
số của dòng hay cột tương ứng.
Ví dụ:
QUANGCAO.INP QUANGCAO.OUT
4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
16
4
C1
C4
D1
D4
Lời giải:
Trước hết ta đưa ra hai nhận xét:
• Mỗi công tắc chỉ cần được tác động nhiều nhất một lần: thật vậy, tác
động một công tắc 2 lần sẽ cho kết quả giống như khi không tác động lên
công tắc.
• Thứ tự tác động lên các công tắc là không quan trọng. Nói cách khác, tác
động một dãy công tắc theo trình tự nào cũng mang lại kết quả như nhau.
Từ hai nhận xét trên, ta thấy mỗi công tắc sẽ có hai khả năng: tác động hoặc không
tác động. Có tất cả M+N công tắc, vậy số khả năng là 2M+N
. Theo giới hạn đề bài ra,
con số này có thể rất lớn. Tuy nhiên để tìm số bóng đèn sáng nhiều nhất, ta chỉ cần
4
duyệt qua 2N
≤ 210
= 1024 khả năng tác động lên các công tắc trên các hàng của
bảng. Với mỗi khả năng này, đối với mỗi cột ta khẳng định được ngay có cần phải
tác động lên công tắc của cột đó hay không: nếu tác động lên công tắc cột mà số
đèn sáng trên cột đó nhiều hơn thì ta sẽ tác động. Đoạn lệnh sau đây thể hiện điều
này. Chú ý trong chương trình dưới đây, ta đánh số cột, hàng bắt đầu từ 0.
for j:=0 to m-1 do begin
dem:=0; {đếm số đèn sáng trên cột j}
for i:=0 to n-1 do
if (b[i,j]=1) then inc(dem);
if (dem<n-dem) then {tác động lên công tắc sẽ cho n-dem bóng đèn sáng}
begin {nếu tác động lên công tắc có lợi hơn:}
batcot(b,j); {tác động!}
inc(t); {tăng biến ghi nhận số lần tác động lên một}
cot[j]:=true; {ghi nhớ rằng đã tác động lên cột j}
end;
end;
Trong 2N
khả năng này, ta sẽ chọn ra khả năng cho số bóng đèn sáng nhiều nhất.
Về cài đặt chương trình, chú ý sử dụng kỹ thuật xử lý bit khi duyệt qua 2N
khả năng
tác động lên các công tắc trên hàng. Dùng kỹ thuật này, chương trình sẽ được viết
nhanh và gọn hơn. Câu lệnh sau đây thực hiện điều này:
for s:=0 to (1 shl n)-1 do {biến s là một dãy N bit thể hiện một khả năng}
Do ta muốn có một dãy N bit đến biến s sẽ chạy từ 0 đến 2N
- 1.
Để kiểm tra trong khả năng s, một công tắc nào đó có được tác động hay không ta
dùng thủ tục kiểm tra một bit có được bật hay không:
function bitbat(s, i: longint): boolean;
begin
bitbat:=((s shr i) and 1)=1;
end;
Chương trình:
const
finp='quangcao.inp';
fout='quangcao.out';
maxn=15; maxm=110;
type bang=array[0..maxn-1, 0..maxm-1] of byte;
var
n, m, kq, sotacdong: longint;
a, b: bang;
hang, luuhang: array[0..MAXN-1] of boolean;
cot, luucot: array[0..MAXM-1] of boolean;
procedure nhap;
5
var i, j: longint;
begin
readln(n,m);
for i:=0 to n-1 do
for j:=0 to m-1 do
read(a[i,j]);
end;
function bitbat(s, i: longint): boolean;
begin
bitbat:=((s shr i) and 1)=1;
end;
procedure batcot(var a: bang; t: longint);
var i: longint;
begin
for i:=0 to n-1 do a[i,t]:=1-a[i,t];
end;
procedure bathang(var a: bang; t: longint);
var i: longint;
begin
for i:=0 to m-1 do a[t,i]:=1-a[t,i];
end;
function sodensang(var a: bang): longint;
var i,j,dem: longint;
begin
dem:=0;
for i:=0 to n-1 do for j:=0 to m-1 do if (a[i,j]=1) then inc(dem);
sodensang:=dem;
end;
procedure xuly;
var s,i,j,dem,k,t: longint;
begin
kq:=0;
for s:=0 to (1 shl n)-1 do begin
b:=a;
t:=0;
fillchar(hang,sizeof(hang),false);
fillchar(cot,sizeof(cot),false);
for i:=0 to n-1 do
if (bitbat(s,i)) then begin
bathang(b,i); inc(t); hang[i]:=true;
end;
for j:=0 to m-1 do begin
dem:=0;
for i:=0 to n-1 do
if (b[i,j]=1) then inc(dem);
6
if (dem<n-dem) then begin batcot(b,j); inc(t); cot[j]:=true; end;
end;
k:=sodensang(b);
if (k>kq) then begin
kq:=k;
sotacdong:=t;
luuhang:=hang;
luucot:=cot;
end;
end;
end;
procedure xuat;
var i: longint;
begin
writeln(kq);
writeln(sotacdong);
for i:=0 to m-1 do
if (luucot[i]) then writeln('C',i+1);
for i:=0 to n-1 do
if (luuhang[i]) then writeln('D',i+1);
end;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
nhap;
xuly;
xuat;
close(input);
close(output);
end.
Năm học 2003-2004
Bài 1: TỔNG LỚN NHẤT tên chương trình: SUM.PAS
Cho một bảng A gồm N x N số nguyên (N ≤ 100), các dòng được đánh số trên
xuống dưới bắt đầu từ 1, các cột được đánh số từ trái qua phải cũng bắt đầu từ 1.
Mỗi số trong bảng có giá trị tuyệt đối không vượt quá 10000. Đường chéo chính của
bảng là đường thẳng nối hai ô (1,1) và (N,N). Như vậy trên bảng có 2N-1 đuờng
chéo song song với đường chéo chính.
Bài toán: Hãy tìm đường chéo song song với đường chéo chính có tổng các phần tử
trên đường chéo đó là lớn nhất.
Dữ liệu vào cho trong file văn bản SUM.INP trong đó:
7
Dòng đầu chứa số N.
Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các phần tử
nằm trên dòng thứ i của bảng A.
Kết quả ghi ra trong file văn bản SUM.OUT trong đó chứa một số nguyên duy nhất là
tổng các phần tử trên đường chéo mà bạn tìm được.
1 2 4 3
3 4 2 5
2 5 4 3
4 3 2 5
Ví dụ: với bảng A như hình vẽ, đường chéo chính chính là đường chéo có tổng lớn
nhất (bằng 14), các file dữ liệu vào/ra lần lượt có nội dung như sau:
SUM.INP SUM.OUT
4
1 2 4 3
3 4 2 5
2 5 4 3
4 3 2 5
14
Lời giải
Để giải bài toán này ta cần biết cách duyệt qua các đường chéo song song với
đường chéo chính của bảng số một cách nhanh gọn và hiệu quả. Có rất nhiều cách
để thực hiện điều này. Sau đây là một cách: để ý rằng trên một đường chéo, hiệu
giữa chỉ số hàng và chỉ số cột là không đổi. Hiệu này nhận giá trị từ 1-N (tương ứng
với đường chéo gồm 1 ô góc trên phải (1,N)) cho đến N-1 (tương ứng với đường
chéo gồm 1 ô góc dưới trái (N,1)). Do đó ta lần lượt xét các giá trị hiệu từ 1-N đến N-
1. Ký hiệu giá trị này là T. Với mỗi giá trị T, ta duyệt qua các cột trên đường chéo
tương ứng. Nếu T≥0 (tương ứng với các đường chéo ở nửa dưới của bảng) thì cột
bắt đầu là 1 còn nếu T<0 thì cột bắt đầu là 1-T.
Đoạn lệnh sau thể hiện cách làm này:
for t:=1-n to n-1 do
if (t>=0) then j:=1 else j:=1-t; {j là biến lưu cột bắt đầu của đường chéo}
s:=0; {tổng các phần tử trên đường chéo}
repeat
s:=s+a[j+t,j]; {cộng giá trị ô hiện thời vào tổng}
inc(j); {sang cột mới}
until (j+t>n) or (j>n); {vượt quá phạm vi của bảng}
if (s>kq) then kq:=s; {cập nhật kết quả}
end;
Chương trình:
8
Đường chéo
const
MAXN=105;
finp='sum.inp';
fout='sum.out';
var
n, i, j, t, s, kq: longint;
a: array[1..MAXN, 1..MAXN] of longint;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
readln(n);
for i:=1 to n do
for j:=1 to n do
read(a[i,j]);
kq:=-maxlongint;
for t:=1-n to n-1 do begin
if (t>=0) then j:=1 else j:=1-t;
s:=0;
repeat
s:=s+a[j+t,j];
inc(j);
until (j+t>n) or (j>n);
if (s>kq) then kq:=s;
end;
writeln(kq);
close(input);
close(output);
end.
Bài 2: SẮP XẾP Tên chương trình: SORT.PAS
Cho một dãy X gồm N số nguyên trong phạm vi từ -10000 đến 10000 (1 ≤N ≤
100000). 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ứ i trong N dòng tiếp theo chứa số thứ i trong dãy X.
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 mỗi số trên một dòng.
Ví dụ:
SORT.INP SORT.OUT
4
3
4
2
5
4
3
2
9
5
Lời giải:
Phương pháp sắp xếp mà chúng ta dùng là sắp xếp đếm (counting sort). Phương
pháp này tận dụng việc giới hạn của các số cần sắp xếp có thể lưu đủ trong bộ nhớ,
trong bài toán này phạm vi các số là -10000..10000. Ta sẽ dùng mảng dem[-
10000..10000] trong đó dem[x] lưu số lần xuất hiện của số x trong dãy số.
Bài toán này được ra với yêu cầu làm trên trình biên dịch Borland Pascal. Với trình
biên dịch cũ này, quản lý bộ nhớ là một điều quan trọng do dung lượng bộ nhớ bị
hạn chế. Chúng tôi sẽ trình bày lời giải bài toán này trên cơ sở bộ nhớ hạn chế đó.
Do mỗi số có thể xuất hiện đến N ≤ 100 000 lần nên mảng dem phải mang kiểu dữ
liệu longint (số nguyên 32 bit) trong Pascal. Trong Borland Pascal, khi khai báo mảng
20000 phần tử longint sẽ bị báo lỗi là không đủ bộ nhớ. Có một cách để giải quyết
điều này:
Khai bảo mảng dem với kiểu dữ liệu word (có giới hạn từ 0..65535) như sau:
var dem: array[-10000..10000] of word;
Ta nhận xét chỉ có nhiều nhất một phần tử của mang dem có thể có giá trị lớn hơn
hoặc bằng 60000, vì tổng số lượng số nhiều nhất là 100 000. Do đó ta quản lý thêm
một biến lưu phần tử đặc biệt có giá trị đếm vượt 60000 này (nếu có). Ta đặt tên
biến này là v. Đoạn lệnh dưới đây đọc vào các số và quản lý dữ liệu:
for i:=1 to n do
begin
readln(x); {đọc vào một số x}
inc(dem[x]); {tăng biến đếm số lần xuất hiện của số x}
if (dem[x]=60000) then {nếu đã có 60000 số x xuất hiện}
begin
v:=x; {lưu lại số x duy nhất này}
dem[x]:=0; {gán lại biến đếm bằng 0 để tránh tràn số}
end;
end;
Đoạn lệnh dưới đây in ra các số đã sắp xếp theo thứ tự giảm dần:
for i:=-10000 downto 10000 do {duyệt qua phạm vi của các số: [-10000,10000])
begin
for j:=1 to dem[i] do {in ra số i với số lần là dem[i]}
writeln(i);
if (v=i) then for j:=1 to 60000 do {nếu i là số đặc biệt thì ta cần in thêm
60000 lần xuất hiện nữa}
writeln(i);
10
end;
Chương trình:
const finp='sort.inp';
fout='sort.out';
var dem: array[-10000..10000] of word;
n, i, v: longint;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
fillchar(dem,sizeof(dem),0);
readln(n);
v:=0;
for i:=1 to n do
begin
readln(x);
inc(dem[x]);
if (dem[x]=60000) then
begin
v:=x;
dem[x]:=0;
end;
end;
for i:=-10000 downto 10000 do
begin
for j:=1 to dem[i] do
writeln(i);
if (v=i) then for j:=1 to 60000 do
writeln(i);
end;
close(input);
close(output);
end.
Bài 3: HÌNH VUÔNG tên chưong trình: SQUARE.PAS
Trên mặt phẳng cho N hình vuông với các cạnh song song với hệ trục toạ độ được
đánh số từ 1 đến N (1 ≤N ≤2000). Hình vuông thứ i được cho bởi toạ độ góc dưới
trái (xi, yi) và toạ độ đỉnh phải trên là (zi, ti). Toạ độ của các đỉnh là các số nguyên
trong phạm vi -10000 đến 10000. Khoảng cách giữa hai hình vuông A và B được
định nghĩa là độ dài đoạn thẳng ngắn nhất trong số các đoạn thẳng mà một đầu mút
thuộc hình vuông A và đầu mút kia thuộc hình vuông B.
11
Yêu cầu: Tìm hai hình vuông xa nhau nhất trong số N hình vuông cho trước.
Dữ liệu: Vào từ file văn bản SQUARE.INP:
• Dòng đầu tiên chứa số N.
• Dòng thứ i trong N dòng tiếp theo chứa 4 số xi, yi, zi và ti.
Kết quả: Ghi ra file văn bản SQUARE.OUT trong đó chứa chỉ số của hai hình vuông
xa nhau nhất mà bạn tìm được.
Ví dụ:
SQUARE.INP SQUARE.OUT
3
1 1 3 3
2 2 5 5
7 1 8 2
1 3
Lời giải
Vấn đề quan trọng nhất trong bài toán này là xây dựng thủ tục tính khoảng cách giữa
hai hình vuông một cách hiệu quả. Để viết thủ tục này, ta sẽ phân chia công việc cần
thực hiện thành nhiều công việc con. Nhưng trước tiên cần nhận xét rằng, luôn tồn
tại một đoạn thẳng ngắn nhất nối giữa hai hình vuông mà có ít nhất một đầu
mút là một trong các đỉnh của hai hình vuông. Thật vậy, với một đoạn thẳng mà
hai đầu mút đều không phải là đỉnh của hình vuông, ta có thể dời hai đầu mút sao
cho đoạn thẳng mới không dài hơn đoạn thẳng cũ và ít nhất một đầu mút sẽ trở
thành đỉnh của hình vuông.
Vậy thủ tục tính khoảng cách giữa hai hình vuông sẽ được xây dựng từ trên xuống
như sau:
• Khoảng cách giữa hai hình vuông bằng khoảng cách ngắn nhất trong số các
khoảng cách từ một trong 8 đỉnh của hai hình vuông đến hình vuông kia
• Khoảng cách từ một điểm đến một hình vuông bằng khoảng cách ngắn nhất
trong số các khoảng cách từ điểm đó đến bốn cạnh của hình vuông
Khi đã có thủ tục tính khoảng cách giữa hai hình vuông, ta chỉ cần duyệt qua tất cả
các cặp hình vuông và chọn ra cặp có khoảng cách xa nhất. Do giới hạn của bài
toán là số hình vuông N ≤ 2000 nên cách làm này là khả thi, thời gian chạy là O(N2
).
Sau này các bạn sẽ được tiếp cận các lời giải hiệu quả hơn.
Chương trình:
const MAXN=2020;
finp='square.inp';
fout='square.out';
var
x1, y1, x2, y2: array[1..MAXN] of longint;
n, luui, luuj: longint;
12
function min(a,b: longint): longint;
begin
if a<b then min:=a else min:=b;
end;
function kc(x,a,b: longint): longint;
begin
if (a<=x) and (x<=b) then kc:=0
else if (x<a) then kc:=a-x
else kc:=x-b;
end;
function kcdoan(x,y,y0,x1,x2: longint): longint;
begin
kcdoan:=sqr(y0-y)+sqr(kc(x,x1,x2));
end;
function kchinhvuong(x,y,x1,y1,x2,y2:longint):longint;
var kq: longint;
begin
kq:=kcdoan(x,y,y1,x1,x2);
kq:=min(kq,kcdoan(x,y,y2,x1,x2));
kq:=min(kq,kcdoan(y,x,x1,y1,y2));
kq:=min(kq,kcdoan(y,x,x2,y1,y2));
kchinhvuong:=kq;
end;
function kc2hinh(i,j: longint): longint;
var kq: longint;
begin
kq:=kchinhvuong(x1[i],y1[i],x1[j],y1[j],x2[j],y2[j]);
kq:=min(kq,kchinhvuong(x1[i],y2[i],x1[j],y1[j],x2[j],y2[j]));
kq:=min(kq,kchinhvuong(x2[i],y1[i],x1[j],y1[j],x2[j],y2[j]));
kq:=min(kq,kchinhvuong(x2[i],y2[i],x1[j],y1[j],x2[j],y2[j]));
if (i<j) then kq:=min(kq,kc2hinh(j,i));
kc2hinh:=kq;
end;
procedure nhap;
var i: longint;
begin
readln(n);
for i:=1 to n do readln(x1[i], y1[i], x2[i], y2[i]);
end;
procedure xuly;
var i, j, d, xanhat: longint;
begin
xanhat:=-1;
13
for i:=1 to n-1 do
for j:=i+1 to n do
begin
d:=kc2hinh(i,j);
writeln(d);
if d>xanhat then
begin
xanhat:=d;
luui:=i;
luuj:=j;
end;
end;
end;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
nhap;
xuly;
writeln(luui,' ',luuj);
close(input);
close(output);
end.
Năm học 2004-2005
BÀI 1: KHOẢNG CÁCH GIỮA HAI SỐ Tên chương trình: DISTANCE.PAS
Với hai chữ số x và t, khoảng cách của chúng được định nghĩa là số nguyên không
âm nhỏ nhất d(x,y) mà khi cộng thêm d(x,y) vào một chữ số nào đó trong hai chữ số
x,y thì kết quả nhận được là một số nguyên có chữ số hàng đơn vị trùng với chữ số
còn lại. Ví dụ: d(2,5)=3 vì 2+3=5, d(5,1)=4 vì 1+4=5, còn d(1,9)=2 vì 9+2 = 11.
Với hai số nguyên dương X và Y có cùng số lượng chữ số, khoảng cách d(X,Y) giữa
hai số X và Y là tổng khoảng cách giữa các cặp chữ số cùng hàng tương ứng.
Ví dụ d(213,419)=d(2,4) + d(1,1) + d(3,9) = 2 + 0 + 4 = 6.
Bài toán: Cho hai số X và Y có cùng lượng chữ số N (0 < N < 100), hãy tìm khoảng
cách d(X,Y).
Dữ liệu vào từ file văn bản DISTANCE.INP trong đó dòng đầu chứa số X; dòng thứ
hai chứa số Y thỏa mãn dàng buộc của bài toán.
Kết quả ghi ra file văn bản DISTANCE.OUT trong đóchứa một số nguyên duy nhất
là kết quả d(X,Y) tìm được.
Ví du:
DISTANCE.INP DISTANCE.OUT
213
419
6
14
Lời giải:
Ta xây dựng hàm d(a,b) tính khoảng cách hai chữ số a và b theo định nghĩa của bài
toán, sau đó dùng hàm này để tính tổng khoảng cách giữa các cặp chữ số của hai
số. Hàm d(a,b) được thể hiện qua đoạn lệnh sau:
function d(a,b:byte):byte;
var i: byte;
begin
for i:=0 to 9 do {duyệt qua các chữ số từ 0 đến 9}
if ((a+i) mod 10=b) or {nếu cộng thêm i vào a mà thu được b}
((b+i) mod 10=a) then {hoặc ngược lại...)
begin
d:=i; {trả về chữ số i}
exit;
end;
end;
Chương trình:
const
finp='distance.inp';
fout='distance.out';
function d(a,b:byte):byte;
var i: byte;
begin
for i:=0 to 9 do
if ((a+i) mod 10=b) or ((b+i) mod 10=a) then
begin
d:=i;
exit;
end;
end;
var x, y: string; i,s: longint;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
readln(x,y);
s:=0;
for i:=1 to length(x) do
s:=s+d(ord(x[i])-ord('0'),ord(y[i])-ord('0'));
writeln(s);
close(input);
15
close(output);
end.
BÀI 2: TẠO BẢNG Tên chương trình: TABLE.PAS
Cho một bảng A gồm N x N số nguyên (N ≤ 100), các dòng được đánh số từ trên
xuống dưới bắt đầu từ 1, các cột được đánh số từ trái qua phải cũng bắt đầu từ 1.
Mỗi số trong bảng có giá trị tuyệt đối không vượt quá 30000. Bảng B được tạo ra từ
bảng A theo qui tắc sau:
Phần tử của B nằm ở dòng I, cột j có giá trị bằng tổng của các số nằm trong ô
(i,j) và các ô kề nó trong bảng A: Bij = Aij+A(i+1)j+A(i-1)j+Ai(j+1)+Ai(j-1)
Chú ý: Các phần tử nằm ngoài bảng được xem như có giá trị bằng 0.
Bài toán: Cho bảng A. Hãy tạo ra bảng B tương ứng.
Dữ liệu vào cho trong file văn bản TABLE.INP trong đó :
• Dòng đầu chứa số N.
• Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các
phần tử nằm trên dòng thứ i của bảng A.
• Các số trên cùng một dòng cách nhau bởi khỏang trắng.
Kết quả ghi ra file văn bản TABLE.OUT cho biết bảng B tạo được có định dạng cùng
một qui cách với file input, nghĩa là:
Dòng đầu chứ số N.
Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các
phần tử nằm trên dòng thứ i của bảng B.
Các số trên cùng một dòng cách nhau bởi khỏang trắng.
Ví dụ:
TABLE.INP TABLE.OUT
4
1 2 3 4
5 6 7 8
9 8 7 6
5 4 3 2
4
8 12 16 15
21 28 31 25
27 34 31 23
18 20 16 11
Lời giải:
Ta in ra các phần tử của mảng B theo công thức như đề bài nêu:
for i:=1 to n do begin
for j:=1 to n do
write(a[i,j]+a[i,j-1]+a[i-1,j]+a[i,j+1]+a[i+1,j],' ');
writeln;
end;
Khi cài đặt chú ý để khai báo dư ra các phần tử biên trên mảng A và đặt chúng bằng
giá trị 0:
16
var
a: array[0..MAXN+1, 0..MAXN+1] of longint;{0,n+1 là chỉ số của các hàng, cột biên}
...
fillchar(a,sizeof(a),0); {tất cả các phần tử, bao gồm phần tử biên sẽ bằng 0}
Chương trình:
const
finp='table.inp';
fout='table.out';
MAXN=105;
var
a: array[0..MAXN+1, 0..MAXN+1] of longint;
n,i,j: longint;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
fillchar(a,sizeof(a),0);
readln(n);
for i:=1 to n do
for j:=1 to n do
read(a[i,j]);
writeln(n);
for i:=1 to n do begin
for j:=1 to n do
write(a[i,j]+a[i,j-1]+a[i-1,j]+a[i,j+1]+a[i+1,j],' ');
writeln;
end;
close(input);
close(output);
end.
BÀI 3: TÌM NGHIỆM Tên chương trình: EQUA.PAS
Xét một phương trình có dạng sau:
x + y+ z =K
trong đó K là một số nguyên dương.
Phương trình này có thể có vô số nghiệm. Tuy nhiên, ở đây người ta chỉ quan tâm
đến các nghiệm (x,y,z) mà trong đó các số x,y,z đều là các số nguyên tố.
Nhắc lại: số tự nhiên p được gọi là số nguyên tố nếu p>1 và p chỉ chia hết cho 1 và
chính nó.
Bài toán: Với số K cho trước (K < 5000), hãy tìm tất cả các bộ số nguyên tố x,y,z (x
≤ y ≤ z) là nghiệm của phương trình trên hoặc cho biết không có nghiệm thoả mãn
yêu cầu bài toán.
17
Dữ liệu vào cho trong file văn bản EQUA.INP trong đó chứa duy nhất số K
Kết quả ghi ra file văn bản EQUA.OUT chứa N + 1 dòng (N là số nghiệm tìm được),
trong đó:
• Dòng thứ i trong N dòng đầu tiên chứa 3 số nguyên cho biết bộ nghiệm thứ i
tìm được.
• Dòng thứ N + 1 chứa 3 số 0 cho biết điểm kết thúc file output.
• Các số trên cùng một dòng cách nhau bởi khoảng trắng.
Ví dụ:
EQUA.INP EQUA.OUT
4 0 0 0
EQUA.INP EQUA.OUT
7 2 2 3
0 0 0
Lời giải
Ta kiểm tra các số nguyên tố từ 1 đến 5000 (giá trị lớn nhất của K) rồi lưu vào một mảng:
for i:=2 to MAXK do
if (nguyento(i)) then begin {nếu i là số nguyên tố}
inc(n);
p[n]:=i; {lưu i vào mảng p}
nt[i]:=true; {đặt lại cờ đánh dấu cho biết i là số nguyên tố}
end;
Sau đó dùng hai vòng lặp qua mảng này để duyệt qua x và y. Mảng đánh dấu nt dùng để kiểm
tra nhanh xem z=K-x-y có phải là số nguyên tố không:
for i:=1 to n do {i là chỉ số của số nguyên tố x trong mảng p}
for j:=i to n do {j là chỉ số của số nguyên tố y trong mảng p}
begin
z:=k-p[i]-p[j]; {z=k-x-y}
if (nt[z]) and (p[j]<z) then {kiểm tra z có phải số nguyên tố không?}
writeln(p[i], ' ',p[j],' ', z); {in ra x, y, z}
end;
Thời gian chạy của chương trình là O(N2
) với N là số lượng số nguyên tố. N vào khoảng 700
nên chương trình hoàn toàn chạy trong thời gian cho phép.
Chương trình:
const finp='equa.inp';
fout='equa.out';
MAXK=5010;
function nguyento(n: longint): boolean;
var i: longint;
begin
nguyento:=false;
18
for i:=2 to trunc(sqrt(n)) do if n mod i = 0 then exit;
nguyento:=true;
end;
var n, k, i, j, z: longint;
nt: array[2..MAXK] of boolean;
p: array[1..MAXK] of longint;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
fillchar(nt,sizeof(nt),0);
for i:=2 to MAXK do
if (nguyento(i)) then begin
inc(n);
p[n]:=i;
nt[i]:=true;
end;
readln(k);
for i:=1 to n do
for j:=i to n do
begin
z:=k-p[i]-p[j];
if (nt[z]) and (p[j]<z) then
writeln(p[i], ' ',p[j],' ', z);
end;
writeln('0 0 0');
close(input);
close(output);
end.
Năm học 2005-2006
Bài 1: Trộn mảng
Cho hai mảng số nguyên dương A và B lần lượt có N và M số (0 < N, M <50 000).
Các phần tử trong cả hai mảng A và B đều được sắp theo thứ tự tăng dần.
Yêu cầu: hãy tạo mảng C gồm N+M phần tử từ tất cả các phần tử của A và B sao
cho các phần tử của C cũng có thứ tự tăng dần.
Dữ liệu cho trong 2 file văn bản có tên A.INP và B.INP.
Dòng đầu của file A.INP chứa số N. Mỗi dòng trong N dòng tiếp theo chứa
1 số nguyên dương ứng với các phần tử của mảng A.
19
Dòng đầu của file B.INP chứa số M. Mỗi dòng trong M dòng tiếp theo chứa
1 số nguyên dương ứng với các phần tử của mảng B.
Kết quả xuất ra file văn bản C.OUT gồm N+M dòng, lần lượt chứa các phần tử của
mảng C.
Ví dụ:
A . INP
3
1
2
5
B . INP
2
2
4
C . OUT
1
2
2
4
5
Lời giải
Trong bài toán này ta hoàn toàn không cần lưu trữ mảng A và B vào bộ nhớ. Thuật
toán của ta sẽ đọc lần lượt các số từ hai mảng A, B, xử lý và in ra trực tiếp mảng C.
Ta quản lý hai biến chạy i và j tương ứng trên mảng A và B. Ban đầu i và j được gán
giá trị 1, nghĩa là vị trí đầu tiên của mảng. Tại một thời điểm, biến chạy i, j cho biết ta
đã đọc đến phần tử Ai và Bj. Quy tắc tăng giá trị biến chạy là như sau:
• Nếu Ai ≤ Bj: in ra giá trị Ai, tăng biến chạy i, đọc giá trị mới từ mảng A. Đoạn
mã sau đây thể hiện điều này:
{biến a, b chỉ phần tử đang xét Ai, Bj trên hai mảng}
if (a<=b) then {điều kiện Ai ≤ Bj}
begin
writeln(fc,a); {in ra Ai như là phần tử tiếp theo của mảng C}
inc(i); {tăng biến i}
if (i<=n) then {nếu vẫn chưa đọc hết mảng A}
readln(fa,a) {đọc số mới}
else
20
a:=maxlongint; {nếu đã đọc hết mảng A, gán a giá trị bằng ∞ để sau này
khi so sánh, chỉ các phần tử của mảng B mới được xét}
end else begin
... {tương tự cho trường hợp Ai > Bj}
end;
• Nếu Ai > Bj: tương tự ta in ra giá trị Bj, tăng biến chạy j và đọc giá trị mới từ
mảng B
Thuật toán kết thúc khi ta đã đọc hết các số, nghĩa là khi hai biến chạy đều vượt ra
khỏi phạm vi của mảng: i > N và j > M. Ta có thể dùng vòng lặp while sau đây để
kiểm tra điều kiện này:
while (i<=n) or (j<=m) do begin
...
end;
Thời gian chạy của thuật toán là tuyến tính theo số phần tử của hai mảng: O(M+N).
Chương trình:
var fa,fb,fc: text;
n, m, i, j, a, b: longint;
begin
assign(fa,'a.inp');
reset(fa);
assign(fb,'b.inp');
reset(fb);
assign(fc,'c.out');
rewrite(fc);
readln(fa,n);
readln(fb,m);
i:=1;
readln(fa,a);
j:=1;
readln(fb,b);
while (i<=n) or (j<=m) do begin
if (a<=b) then
begin
writeln(fc,a);
inc(i);
if (i<=n) then
readln(fa,a)
else
a:=maxlongint;
end else begin
writeln(fc,b);
inc(j);
if (j<=m) then
readln(fb,b)
else
21
b:=maxlongint;
end;
end;
close(fa);
close(fb);
close(fc);
end.
Bài 2:Hình chữ nhật
Cho N hình chữ nhật (2 <N< 500) có các cạnh song song với hai trục tọa độ và tọa
độ các đỉnh đều nguyên. Các hình chữ nhật được đánh số từ 1 đến N.
Yêu cầu: Hãy tìm hai hình chữ nhật mà phần giao nhau của chúng có diện tích lớn
nhất.
Dữ lieu cho trong file văn bản có tên là HCN.INP:
Dòng đầu chứa số N.
Dòng thứ I trong N dòng tiếp theo mô tả hình chữ nhật thứ I, chứa 4 số
nguyên x1, y1, x2, y2 ứng với các hòanh độ và tung độ của các hình chữ nhật (-
10000< x1, < x2 <10000; -10000< y1, < y2 <10000).
Kết quả xuất ra file văn bản HCN.OUT gồm 1 dòng duy nhất, chứa 2 số nguyên
dương cho biết chỉ số của 2 hình chữ nhật tìm được.
Ví dụ:
HCN . INP
3
1 1 5 5
-5 -5 5 5
10 10 1000 1000
Lời giải:
Chúng ta cần viết thủ tục tính diện tích phần giao giữa hai hình chữ nhật. Chú ý phần
giao của hai hình chữ nhật cũng là một hình chữ nhật và có chiều dài/rộng bằng
phần giao của hai chiều dài/rộng tương ứng. Do đó ta chỉ cần viết thủ tục tìm phần
giao giữa hai đoạn thẳng trên trục số và sử dụng.
Thủ tục sau tìm giao giữa hai đoạn thẳng (a1,b1) và (a2,b2) trên trục số:
function giaodoan(a1,b1,a2,b2:longint):longint;
begin
if (b1<=a2) or (a1>=b2) then
giaodoan:=0 {hai đoạn không giao nhau}
else
giaodoan:=min(b1,b2)-max(a1,a2); {hai đoạn giao nhau}
end;
22
HCN . OUT
1 2
Thủ tục giaohcn sau áp dụng thủ tục giaodoan để tìm diện tích phần giao của hai
hình chữ nhật:
function giaohcn(i,j: longint): longint;
begin
{phần giao là một hình chữ nhật có chiều dài/rộng là phần giao của hai chiều
dài/rộng ban đầu}
giaohcn:=giaodoan(x1[i],x2[i],x1[j],x2[j])*giaodoan(y1[i],y2[i],y1[j],y2[j]);
end;
Sau khi có thủ tục tính diện tích phần giao giữa hai hình chữ nhật, ta xét qua tất cả
các cặp hình chữ nhật và chọn ra cặp có phần giao lớn nhất:
for i:=1 to n-1 do
for j:=i+1 to n do
begin
s:=giaohcn(i,j);
if s>kq then {tìm được hai hình có diện tích phần giao lớn hơn}
begin
kq:=s; {cập nhật kết quả}
luui:=i; {lưu lại chỉ số của hai hình}
luuj:=j;
end;
end;
Chương trình:
const MAXN=505;
finp='hcn.inp';
fout='hcn.out';
var
x1,y1,x2,y2: array[1..MAXN] of longint;
function min(a,b:longint):longint;begin if a<b then min:=a else min:=b; end;
function max(a,b:longint):longint;begin if a>b then max:=a else max:=b; end;
function giaodoan(a1,b1,a2,b2:longint):longint;
begin
if (b1<=a2) or (a1>=b2) then giaodoan:=0 else giaodoan:=min(b1,b2)-max(a1,a2);
end;
function giaohcn(i,j: longint): longint;
begin
giaohcn:=giaodoan(x1[i],x2[i],x1[j],x2[j])*giaodoan(y1[i],y2[i],y1[j],y2[j]);
end;
var n, kq, s, i, j, luui, luuj: longint;
begin
assign(input,finp);
reset(input);
assign(output,fout);
23
rewrite(output);
kq:=0;
readln(n);
for i:=1 to n do
readln(x1[i],y1[i],x2[i],y2[i]);
for i:=1 to n-1 do
for j:=i+1 to n do
begin
s:=giaohcn(i,j);
if s>kq then
begin
kq:=s;
luui:=i;
luuj:=j;
end;
end;
writeln(kq);
writeln(luui,' ',luuj);
close(input);
close(output);
end.
Bài 3: So sánh
Cho 2 số nguyên dương A,B (0<A, B< 10100
).
Yêu cầu: hãy so sánh giá trị của 2 số.
Dữ liệu cho trong file văn bản có tện SO.INP gồm 2 dòng:
Dòng đầu chứa số A.
Dòng thứ 2 chứa số B.
Kết quả xuất ra file văn bản SO.OUT gồm 1 dòng duy nhất, chứa số -1,0 hoặc 1 lần
lượt tương ứng với các trường hợp sau: A < B, A = B, và A > B.
Ví dụ:
SO.INP SO.OUT
12345678900000001
12345678900000000
1
Lời giải:
Thủ tục so sánh hai số A, B có thể được xây dựng như sau:
• Xem A, B là xâu ký tự. Nếu độ dài của hai xâu khác nhau, ta thêm chữ số 0
vào đầu xâu ngắn hơn cho đến khi độ dài của hai xâu bằng nhau :
while (length(a)<length(b)) do a:='0'+a;
while (length(b)<length(a)) do b:='0'+b;
24
• Sau đó ta duyệt các chữ số từ trái sang phải, nếu chữ số tương ứng của A
bé hơn/lớn hơn của B thì kết luận A<B hay A>B và thoát khỏi thủ tục. Nếu
tất cả các cặp chữ số đều giống nhau thì ta kết luận A=B.
for i:=1 to length(a) do
if (a[i]<b[i]) then begin
sosanh:=-1; {kết luận A < B}
exit; {thoát khỏi thủ tục}
end else if (a[i]>b[i]) then
begin
sosanh:=1; {kết luận A > B}
exit; {thoát khỏi thủ tục}
end;
sosanh:=0; {kết luận A = B}
Chương trình:
const finp='so.inp';
fout='so.out';
function sosanh(a,b:string):longint;
var i: longint;
begin
while (length(a)<length(b)) do a:='0'+a;
while (length(b)<length(a)) do b:='0'+b;
for i:=1 to length(a) do
if (a[i]<b[i]) then begin
sosanh:=-1;
exit;
end else if (a[i]>b[i]) then
begin
sosanh:=1;
exit;
end;
sosanh:=0;
end;
var
a, b: string;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(a);
readln(b);
writeln(sosanh(a,b));
close(input);
close(output);
25
end.
Bài 4: Bảng vuông
Cho một bảng vuông các số nguyên kích thước NxN (2 < N< 100) mà mỗi phần tử là
một số nguyên không âm và giá trị không vượt quá 100.
Yêu cầu: hãy tìm một bảng vuônmg con của bảng đã cho mà các phần tử của nó
chứa toàn số dương và tổng các phần tử thuộc bảng con này có giá trị lớn nhất.
Dữ liệu chop trong file văn bản có tên BANG.INP:
Dòng đầu chứa số N.
Dòng thứ I trong N dòng tiếp theo chứa N số nguyên dương ứng với dòng
thứ i của bảng.
Kết quả xuất ra file văn bản BANG.OUT chứa 1 số nguyên duy nhất chứa giá trọ
tổng lớn nhất tìm được.
Ví dụ:
BANG . INP BANG . OUT
3
1 1 0
1 2 1
1 1 2
6
Lời giải
Để giải quyết bài toán này, ta dùng một phương pháp thường gặp khi xử lý các phép
tính tổng trên bảng số: dùng bảng lưu tổng bộ phận. Cụ thể là, ta sẽ dùng bảng s với
ý nghĩa: s[i,j] là tổng các số trên bảng từ ô (1,1) đến ô (i,j). Mỗi khi đọc phần tử (i, j),
bảng s sẽ được cập nhật theo công thức
s[i,j]:=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+a[i,j];
Và để tính tổng các số trong hình chữ nhật (i1,j1) đến (i2,j2) ta dùng công thức:
s[i2,j2]-s[i1-1,j2]-s[i2,j1-1]+s[i1-1,j1-1]
Để xử lý điều kiện bảng số chứa toàn số dương, ta dùng thêm bảng c theo kỹ thuật
tương tự bảng s với ý nghĩa: c[i,j] là số số 0 trên bảng ban đầu trong hình chữ nhật
từ ô (1,1) đến ô (i,j). Để xét xem một vùng hình chữ nhật có gồm toàn số dương hay
không, ta dùng công thức tính tổng như trên và xem kết quả có bằng 0 hay không.
Để duyệt qua các hình vuông con trên bảng số, ta xét tất cả các đỉnh trên trái (i,j) và
độ dài cạnh hình vuông L. Tổng các số trong hình vuông và điều kiện chứa toàn số
dương được tính theo công thức như trình bày ở trên. Thời gian thực hiện của thuật
toán là O(N3
), hoàn toàn khả thi với giới hạn N ≤ 100.
Chương trình mẫu
26
const MAXN=105;
finp='bang.inp';
fout='bang.out';
type bang=array[0..MAXN, 0..MAXN] of longint;
function max(a,b:longint): longint;
begin
if a>b then max:=a else max:=b;
end;
function sum(a: bang; i1,j1,i2,j2: longint): longint;
begin
sum:=a[i2,j2]-a[i1-1,j2]-a[i2,j1-1]+a[i1-1,j1-1];
end;
var
c, s: bang;
n,i,j,l,x,i1,j1,i2,j2,kq: longint;
begin
assign(input,'bang.inp');
reset(input);
assign(output,'bang.out');
rewrite(output);
fillchar(s,sizeof(s),0);
fillchar(c,sizeof(c),0);
readln(n);
for i:=1 to n do
for j:=1 to n do begin
read(x);
s[i,j]:=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+x;
c[i,j]:=c[i-1,j]+c[i,j-1]-c[i-1,j-1];
if (x=0) then inc(c[i,j]);
end;
kq:=0;
for i1:=1 to n do
for j1:=1 to n do
begin
l:=0;
repeat
i2:=i1+l;
j2:=j1+l;
if sum(c,i1,j1,i2,j2)=0 then
kq:=max(kq,sum(s,i1,j1,i2,j2))
else
break;
inc(l);
27
until (i2>n) or (j2>n);
end;
writeln(kq);
close(input);
close(output);
end.
Năm học 2006-2007
ĐOẠN CON DÀI NHẤT
(Bài 1 – Tuyển sinh 2006 - 2007)
Cho chuỗi kí tự S gồm toàn các chữ cái in hoa (A…Z) với độ dài không vượt quá
255. Hãy tìm đoạn con các kí tự liên tiếp dài nhất sao cho không có kí tự nào xuất
hiện nhiều hơn một lần. Trong trường hợp có nhiều hơn một đoạn con có cùng chiều
dài dài nhất, hãy chỉ ra đoạn xuất hiện đầu tiên trong chuỗi S.
Dữ liệu: Vào từ văn bản SUBSTR.INP gồm một dòng duy nhất chứa chuỗi S.
Kết quả: Ghi ra file văn bản SUBSTR.OUT hai số nguyên P và L tương ứng là vị trí
và chiều dài của đoạn con dài nhất tìm được (kí tự đầu tiên trong chuỗi có vị trí là 1).
Ví dụ:
SUBSTR.INP SUBSTR.OUT
ABABCDAC 3 4
Lời giải
Ta duyệt qua lần lượt các ký tự của chuỗi. Khi duyệt qua ký tự thứ i, ta sẽ tìm cách
xây dựng chuỗi dài nhất không có ký tự nào xuất hiện hai lần và kết thúc tại vị
trí i. Để xây dựng được chuỗi này, ta cần lưu trữ các thông tin sau đây:
• Vị trí bắt đầu của chuỗi dài nhất mà không có ký tự nào xuất hiện hai lần, ta
ký hiệu là p.
• Mảng b['A'..'Z'] trong đó với ký tự c thì b[c] lưu vị trí xuất hiện cuối cùng của
ký tự c.
Khi duyệt qua ký tự thứ i (ký hiệu là s[i]):
• Nếu vị trí xuất hiện cuối cùng của ký tự này không nhỏ hơn p, nghĩa là ký tự
này sẽ xuất hiện 2 lần và làm cho chuỗi đang xét trở nên không hợp lệ. Khi
đó ta cập nhật lại vị trí bắt đầu p bằng b[s[i]]+1.
• Ta cập nhật lại giá trị i cho b[s[i]] để đảm bảo ý nghĩa của mảng b
• Chuỗi đang xét sẽ bắt đầu từ vị trí p và kết thúc tại vị trí i, do đó chuỗi này có
độ dài là i-p+1, ta dùng giá trị này so sánh với kết quả để tìm ra chuỗi dài
nhất.
Đoạn chương trình sau đây thể hiện thuật toán:
p:=1; {gán giá trị khởi tạo cho p: vị trí bắt đầu là 1}
for i:=1 to length(s) do {duyệt qua từng ký tự của chuỗi}
begin
28
if (b[s[i]]>=p) then {ký tự s[i] xuất hiện hai lần trong chuỗi đang xét!}
p:=b[s[i]]+1; {cập nhật lại vị trí bắt đầu mới}
b[s[i]]:=i; {cập nhật lại vị trí xuất hiện của ký tự s[i]}
if (i-p+1>l) then begin {i-p+1 là độ dài của chuỗi đang xét}
{so sánh với độ dài lớn nhất l}
luup:=p; {lưu lại vị trí bắt đầu}
l:=i-p+1; {cập nhật lại độ dài lớn nhất}
end;
end;
Chương trình:
const finp='substr.inp';
fout='substr.out';
var
b:array['A'..'Z'] of longint;
s:string;
p,i,l,luup: longint;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(s);
fillchar(b,sizeof(b),0);
p:=1;
for i:=1 to length(s) do
begin
if (b[s[i]]>=p) then
p:=b[s[i]]+1;
b[s[i]]:=i;
if (i-p+1>l) then begin
luup:=p;
l:=i-p+1;
end;
end;
writeln(luup,' ',l);
close(input);
close(output);
end.
ĐƯỜNG ĐI
(Bài 2 – Tuyển sinh 2006 - 2007)
Một con robot di chuyển theo một chương trình định sẵn trên mặt phẳng toạ độ.
Chương trình này được thể hiện dưới dạng một dãy N lệnh (1 ≤N ≤3000). Các lệnh
thuộc một trong các dạng sau:
• F S: Đi thẳng theo hướng hiện tại S bước.
• R S: Rẽ phải 900
và đi S bước.
• L S: Rẽ trái 900
và đi S bước.
29
Yêu cầu: Cho một chương trình điều khiển robot, hãy xác định chiều dài T đoạn
đường mà con robot đã đi được, biết mỗi bước của nó dài d(cm). Ban đầu con robot
đứng tại vị trí (0,0) và hướng theo chiều dương của trục hoành.
Dữ liệu: Vào từ file văn bản PATH.INP:
• Dòng đầu tiên chứa 2 số nguyên dương N và d.
• N dòng tiếp theo, mỗi dòng chứa một lệnh theo quy cách nêu trên.
Kết quả: Ghi ra file PATH.OUT chứa chiều dài T tìm được.
Ví dụ:
PATH.INP PATH.OUT
4 1
F 5
R 7
F 2
L 9
23
Lời giải:
Kết quả của bài toán này bằng tích của d và tổng của độ dài các bước đi. Các lệnh
F, R, L không ảnh hưởng đến kết quả của bài toán và chỉ được đưa ra để kiểm tra
thí sinh có thuần thục trong việc đọc dữ liệu hay không.
Mỗi câu lệnh sẽ gồm hai ký tự (một chữ cái chỉ lệnh và một khoảng trắng) và một số,
do đó ta đọc vào hai biến kiểu ký tự và một biến kiểu số nguyên:
readln(c,c,x);
Ta sẽ sử dụng biến x, còn c chỉ là biến tạm không được dùng đến.
Chương trình mẫu
const finp='path.inp';
fout='path.out';
var i, n, d, x, s: longint;
c: char;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(n,d);
for i:=1 to n do begin
readln(c,c,x);
s:=s+x;
end;
writeln(x);
close(input);
close(output);
end.
30
ĐẾM VÙNG
(Bài 3 – Tuyển sinh 2006 - 2007)
Một khu vườn hình chữ nhật được chia thành NM × ô đơn vị. Các dòng đánh số
từ 1 tới M từ trên xuống dưới, các cột đánh số từ 1 đến N từ trái sang phải. Ô nằm ở
hàng i, cột j được gọi là ô (i,j). Người ta có đắp K lối đi trên mảnh vườn đó, lối đi thứ
i là một dãy các ô liên tiếp nhau theo đường ngang hoặc đường dọc, và được cho
bởi 4 số nguyên dương xi, yi, zi và ti trong đó (xi, yi) là vị trí của ô đầu, còn (zi, ti) là vị
trí của ô cuối của lối đi. Các lối đi chia khu vườn thành các miền. Mỗi miền là một tập
tất cả các ô không thuộc các lối đi sao cho hai ô bất kì trong đó có thể đi tới bằng
cách di chuyển qua các ô chung cạnh và không phải là ô thuộc lối đi.
Yêu cầu: Hãy xác định số miền S mà các lối đi chia khu vườn.
Dữ liệu: Vào từ file văn bản REGIONS.INP trong đó:
• Dòng đầu chứa 3 số M, N, K.
• Dòng thứ i trong K dòng tiếp theo chứa 4 số xác định lối đi thứ i: xi, yi, zi, ti.
Kết quả: Ghi ra file văn bản REGIONS.OUT số S tìm được.
Ví dụ:
REGIONS.INP REGIONS.OUT
10 10 2
5 1 5 10
1 5 7 5
3
SỐ ƯỚC
(Bài 4 – Tuyển sinh 2006 - 2007)
Cho số nguyên dương N. Giai thừa của N, kí hiệu là N!, là tích của các số tự nhiên
từ 1 đến N. Gọi T là số lượng ước lớn hơn 1 của N!. Ví dụ với N = 4, ta có 4! = 24.
Như vậy 4! có 7 ước lớn hơn 1 là: 2, 3, 4, 6, 8, 12, 24.
Yêu cầu: Cho N, hãy xác định T.
Dữ liệu: Vào từ file văn bản DIVISORS.INP trong đó chứa duy nhất số N (N ≤20,
trong đó 50% số test có N ≤10).
Kết quả: Ghi ra file văn bản DIVISORS.OUT số T tìm được.
Ví dụ:
DIVISORS.INP DIVISORS.OUT
4 7
Lời Giải
Nếu một số nguyên m được phân tích ra thừa số nguyên tố dưới dạng
m=p1
a1
p2
a2
...pk
ak
thì số ước số của m bằng tích (a1+1)(a2+1)...(ak+1). Ta sẽ tìm cách
phân tích n! ra thừa số nguyên tố. Các ước số nguyên tố của n! chỉ nằm trong phạm
vi từ 2 đến n, do đó ta duyệt qua tất cả các số nguyên tố trong phạm vi này. Với số
nguyên tố p, số mũ của nó trong biểu diễn nguyên tố của n! bằng:
31
[n/p] + [n/p2
] + [n/p3
] + ...
Trong đó [n/p] ký hiệu phần nguyên của n/p, nói cách khác trong ngôn ngữ Pascal
[n/p] bằng n div p. Công thức trên được giải thích như sau: [n/p] là số lượng số từ 1
đến n chia hết cho p, [n/p2
] là số lượng số từ 1 đến n chia hết cho p2
,... mỗi số này
đều đóng góp một thừa số nguyên tố p phân biệt cho n!.
Đoạn chương trình sau đây thể hiện thuật toán:
for i:=2 to n do if nguyento(i) then {duyệt qua các số nguyên tố từ 2 đến n}
begin
a:=0;
j:=i; {biến j sẽ duyệt qua các số mũ của i là i, i2
, i3
,...}
while (j<=n) do {đến khi j>n thì dừng lại vì khi đó [n/j] sẽ luôn bằng 0}
begin
a:=a+n div j;
j:=j*i;
end;
{đến đây a=[n/i]+[n/i2
]+... chính là số thừa số i
trong biểu diễn nguyên tố của n!}
d:=d*(a+1); {nhân thêm (a+1) vào số ước, theo công thức tính số ước nêu trên}
end;
Chương trình:
function nguyento(n: longint): boolean;
var i: longint;
begin
nguyento:=false;
for i:=2 to trunc(sqrt(n)) do
if n mod i = 0 then exit;
nguyento:=true;
end;
var
n,i,j,a,d: longint;
begin
readln(n);
d:=1;
for i:=2 to n do if nguyento(i) then
begin
a:=0;
j:=i;
while (j<=n) do
begin
a:=a+n div j;
j:=j*i;
end;
d:=d*(a+1);
end;
writeln(d-1);
end.
32
Năm học 2007-2008
TÍCH LỚN NHẤT
(Bài 1 – Tuyển sinh 2007 - 2008)
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á 30000.
Kết quả: Ghi ra file văn bản TICHMAX.OUT một số duy nhất T.
Ví dụ:
TICHMAX.INP TICHMAX.OUT
9
3 5 1 7 9 0 9 -3 10
810
Lời giải
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ự.
Chú ý vì kết quả có thể vượt quá kiểu số nguyên 32 bit nên ta cần khai báo với kiểu
số nguyên 64 bit (trình biên dịch Free Pascal hỗ trợ kiểu số nguyên 64 bit với tên gọi
int64):
var
33
...
kq:int64;
...
kq:=max(max1*max2*max3,min1*min2*max1);
Chương trình:
const finp='tichmax.inp';
fout='tichmax.out';
var max1,max2,max3,min1,min2,x,i,n:longint;
kq:int64;
function max(a,b:longint):longint;
begin
if a>b then max:=a else max:=b;
end;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
max1:=-maxlongint;
max2:=max1;
max3:=max1;
readln(n);
for i:=1 to n do begin
read(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
begin
max3:=x;
end;
if (x<=min1) then
begin
min2:=min1;
min1:=x;
end else if (x<=min2) then
min2:=x;
end;
34
kq:=max(max1*max2*max3,min1*min2*max1);
writeln(kq);
close(input);
close(output);
end.
TÌM DÃY K
(Bài 2 – Tuyển sinh 2007 - 2008)
Cho một xâu S có độ dài N. Với mỗi số nguyên i (1 ≤i ≤N), xét xâu con Si gồm i kí
tự đầu của xâu S. Người ta cần xác định giá trị Ki cho mỗi xâu Si là giá trị lớn nhất
thỏa mãn điều kiện sau:
Nếu xâu X là xâu con gồm Ki kí tự đầu của xâu Si và tạo xâu Y bằng
cách viết Ki kí tự cuối cùng của xâu Si theo thứ tự ngược từ cuối lên
đầu ta có X = Y.
Ví dụ, nếu S = ‘acbaca’ thì S4 = ‘acba’ và vì vậy K4 = 1, vì khi đó X = Y = ‘a’.
S6 = ‘acbaca’ và vì vậy K6 = 2 do khi đó X = Y = ‘ac’.
Yêu cầu: Cho xâu S độ dài N, tìm các số K1, K2, …, KN.
Dữ liệu: Vào từ file văn bản DAYK.INP:
• Dòng đầu tiên chứa số nguyên dương N (N ≤255).
• Dòng thứ hai chứa xâu S độ dài N.
Kết quả: Ghi ra file văn bản DAYK.OUT trên một dòng duy nhất dãy số K1, K2, …, KN.
Ví dụ:
DAYK.INP DAYK.OUT
6
acbaca
1 0 0 1 0 2
Lời giải
Ta dùng vòng lặp i từ 1 đến N, với mỗi giá trị i ta tìm cách xác định số Ki theo như
yêu cầu của đề bài. Số Ki chính là phần dài nhất mà khi nhìn từ trái sang phải và từ
phải sang trái của chuỗi Si đều như nhau, nói cách khác trên chuỗi Si ký tự đầu tiên
bằng ký tự cuối, ký tự thứ hai bằng ký tự kề cuối, ..., ký tự thứ Ki bằng ký tự thứ i-Ki.
Để xác định Ki ta dùng vòng lặp while tăng dần giá trị của Ki khi nào điều kiện
s[1+k]=s[i-k] còn đúng:
k:=0;
while (s[1+k]=s[i-k]) do inc(k);
Chương trình:
const finp='dayk.inp';
fout='dayk.out';
35
var n, i, k: longint;
s: string;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(n);
readln(s);
for i:=1 to n do
begin
k:=0;
while (s[1+k]=s[i-k]) do inc(k);
write(k,' ');
end;
close(input);
close(output);
end.
TÌM SỐ ÂM LỚN NHẤT
(Bài 3 – Tuyển sinh 2007 - 2008)
Cho một dãy gồm N số nguyên a1, a2, …, aN, mỗi số có giá trị tuyệt đối không vượt
quá 105
.
Yêu cầu: Hãy tìm số âm lớn nhất X trong dãy.
Dữ liệu: Vào từ file văn bản SOAM.INP:
• Dòng đầu tiên chứa số nguyên dương N (1 ≤N ≤105
).
• N dòng tiếp theo, dòng thứ i chứa số ai.
Kết quả: Ghi ra file văn bản SOAM.OUT trên một dòng duy nhất số X tìm được.
Trong trường hợp không có lời giải, ghi ra số 0.
Ví dụ:
SOAM.INP SOAM.OUT
5
-4
3
2
-5
7
-4
Lời giải:
Ta đọc qua các số của dãy và tìm số lớn nhất, tuy nhiên chỉ các số âm mới được xét
đến. Ta dùng thêm một cờ để kiểm tra trong dãy có tồn tại số âm hay không.
36
if (x<0) then {chỉ xét số âm}
begin
co:=true; {đánh dấu cờ là có tồn tại số âm}
if (x>kq) then kq:=x; {cập nhật với kết quả lớn nhất}
end;
Chương trình:
const finp='soam.inp';
fout='soam.out';
var n, kq, i, x: longint;
co: boolean;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
co:=false;
readln(n);
kq:=-maxlongint;
for i:=1 to n do
begin
readln(x);
if (x<0) then
begin
co:=true;
if (x>kq) then kq:=x;
end;
end;
if co then writeln(kq) else writeln(0);
end.
DÃY HÌNH CHỮ NHẬT LỒNG NHAU
(Bài 4 – Tuyển sinh 2007 - 2008)
Trên mặt phẳng tọa độ cho N hình chữ nhật với các cạnh song song với hệ trục tọa
độ, các hình chữ nhật được đánh số từ 1 tới N. Hình chữ nhật thứ i được cho bởi 4
số nguyên dương xi1, yi1, xi2, yi2, trong đó (xi1, yi1) là tọa độ đỉnh trái dưới, còn (xi2, yi2)
là tọa độ đỉnh phải trên. Ta nói rằng hình chữ nhật thứ I nằm trong hình chữ nhật thứ
j nếu trên mặt phẳng tọa độ, mọi điểm của hình chữ nhật i đều thuộc hình chữ nhật j.
Yêu cầu: Với N hình chữ nhật cho trước, hãy tìm K hình chữ nhật với chỉ số i1, i2, …,
iK sao cho hình i1 nằm trong hình i2, hình i2 nằm trong hình i3, …, hình iK-1 nằm trong
hình iK và K là lớn nhất. Biết rằng hai hình chữ nhật bất kì trong N hình chữ nhật đã
cho hoặc rời nhau hoặc một trong hai hình nằm trong hình còn lại.
Dữ liệu: Vào từ file văn bản HCN.INP:
37
• Dòng đầu tiên chứa số nguyên dương N (1 ≤N ≤100).
• N dòng tiếp theo, dòng thứ i chứa 4 số nguyên dương xi1, yi1, xi2, yi2.
Kết quả: Ghi ra file văn bản HCN.OUT số K tìm được.
Ví dụ:
HCN.INP HCN.OUT
3
1 1 7 4
3 1 6 6
2 2 5 4
2
Lời giải:
Bước 1, ta sắp xếp các hình chữ nhật theo thứ tự giảm dần của diện tích. Điều này
đảm bảo một hình chữ nhật chỉ có thể bị bao bởi một hình chữ nhật xuất hiện phía
trước nó. Đoạn chương trình sau thực hiện việc sắp xếp:
for i:=1 to n-1 do
for j:=i+1 to n do
if (dientich(h[i])<dientich(h[j])) then
begin
tam:=h[i];h[i]:=h[j];h[j]:=tam;
end;
Giả sử các hình chữ nhật đã được sắp xếp là h1, h2, ..., hn. Ta gọi f[i] là độ dài lớn
nhất của chuỗi hình chữ nhật lồng nhau kết thúc tại hình hi. Để tính f[i], ta tìm vị trí j
lớn nhất sao cho j < i là hình chữ nhật hj bao hình chữ nhật hi, khi đó f[i]=f[j]+1. Nếu
không tồn tại vị trí j thì hình chữ nhật hi không bị bao bởi hình chữ nhật nào và f[i]=1:
f[i]:=1; {khởi tạo giá trị ban đầu cho f[i]}
for j:=i-1 downto 1 do
if bao(h[j],h[i]) then {j là vị trí lớn nhất mà hình chữ nhật
h[j] bao hình chữ nhật h[i]}
begin
f[i]:=f[j]+1; {tính f[i] theo f[j]}
break; {thoát khỏi vòng lặp}
end;
if (f[i]>kq) then kq:=f[i]; {cập nhật lại kết qủa}
Chương trình:
const
finp='hcn.inp';
fout='hcn.out';
MAXN=105;
type hcn=record x1,y1,x2,y2: longint; end;
var
h: array[1..MAXN] of hcn;
f: array[1..MAXN] of longint;
i,j,n,kq: longint;
38
tam:hcn;
function dientich(a:hcn):longint;
begin
with a do dientich:=(x2-x1)*(y2-y1);
end;
function bao(a,b:hcn):boolean;
begin
bao:=(a.x1<=b.x1) and (b.x2<=a.x2) and
(a.y1<=b.y1) and (b.y2<=a.y2);
end;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(n);
for i:=1 to n do
with h[i] do
readln(x1,y1,x2,y2);
for i:=1 to n-1 do
for j:=i+1 to n do
if (dientich(h[i])<dientich(h[j])) then
begin
tam:=h[i];h[i]:=h[j];h[j]:=tam;
end;
kq:=0;
for i:=1 to n do
begin
f[i]:=1;
for j:=i-1 downto 1 do
if bao(h[j],h[i]) then
begin
f[i]:=f[j]+1;
break;
end;
if (f[i]>kq) then kq:=f[i];
end;
writeln(kq);
close(input);
close(output);
end.
Năm học 2008-2009
Bài 1: Mã hoá
39
Để quản lý tốt các hồ sơ trong kỳ thi tuyển sinh, hội đồng tuyển sinh trường PTNK đã
quyết định đánh số các hồ sơ theo một phương pháp khoa học. Mã hồ sơ của thí
sinh là một chuỗi gồm 10 chữ số. Tuy nhiên không phải bất kỳ chuỗi 10 chữ số nào
cũng là mã hồ sơ hợp lệ bởi vì hội đồng tuyển sinh đưa ra một quy định ràng buộc
chặt chẽ cho các chữ số đó. Nếu M=a1a2..a10 là một mã hồ sơ thì M phải thỏa mãn
ràng buộc:
Nếu đặt S(M)=1a1+2a2+3a3+…+10a10 thì S(M) phải là một số chia hết cho 11.
Nhờ quy định này, trong những trường hợp do sơ xuất có một chữ số trong mã hồ
sơ bị mờ, không đọc được thì ta vẫn có thể xác định được giá trị của nó. Ví dụ như:
(quy ước ? là chữ số bị mờ):
• Với M=00000000?1 thì có thể suy ra chữ số bị mờ là 5 vì theo ràng buộc, để
S(M) là một số chia hết cho 11 nó chỉ có thể có giá trị là 55.
• Tương tự với M=00000001?1 thì có thể suy ra chữ số bị mờ là 9.
• Tương tự với M=00722?0858 thì có thể suy ra chữ số bị mờ là 6.
Yêu cầu: Hãy viết chương trình giúp hội đồng tuyển sinh suy ra được chữ số bị mờ
trong mã hồ sơ.
Dữ liệu: Vào từ file văn bản ENCODE.INP có chứa mã hồ sơ có 1 chữ số bị mờ
được thay bằng dấu chấm hỏi.
Kết quả: Ghi ra file văn bản ENCODE.OUT chứa giá trị của chữ số bị mờ trong mã
hồ sơ đã cho.
Ví dụ:
ENCODE.INP
00000000?1
ENCODE.OUT
5
00000001?1
9
00722?0858
6
Lời giải:
Ta thử thay từng chữ số từ 0 đến 9 vào vị trí "?" và tính xem điều kiện S(M) chia hết
cho 11 có được thoả mãn hay không như đoạn chương trình sau đây:
for i:=1 to 10 do
{r là tổng các số hạng của S(M) trừ vị trí có dấu ‘?’}
if (s[i]<>'?') then r:=(r+i*(ord(s[i])-ord('0'))) mod 11;
i:=pos('?',s); {i là vị trí của dấu ‘?’}
for d:=0 to 9 do {duyệt qua tất cả các chữ số từ 0 đến 9}
if (r+i*d) mod 11 = 0 then {kiểm tra xem S(M) có chia hết cho 11}
begin
writeln(d); {d chính là chữ số cần tìm}
break; {thoát khỏi vòng lặp}
end;
Chương trình:
40
const
finp='encode.inp';
fout='encode.out';
var
s: string;
i, r, d: longint;
c: char;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(s);
for i:=1 to 10 do
if (s[i]<>'?') then r:=(r+i*(ord(s[i])-ord('0'))) mod 11;
i:=pos('?',s);
for d:=0 to 9 do
if (r+i*d) mod 11 = 0 then
begin
writeln(d);
break;
end;
close(input);
close(output);
end.
Bài 2: Biến đổi dãy số
Cho dãy các số nguyên khác nhau đôi một a gồm n số a1, a2, ..., an và dãy số nguyên
b gồm n số b1, b2, ..., bn. Trên dãy số a ta có thể áp dụng phép biến đổi T(i) thực hiện
phép hoán vị giá trị hai phần tử ai và ai+1 (0<i<n) .Vấn đề đặt ra là có tồn tại hay
không một dãy các phép biến đổi Ti1, Ti2, …, Tik sao cho khi áp dụng, dãy a ban đầu
sẽ biến thành dãy b.
Ví dụ nếu dãy a là 1, 3, 2, 4 và dãy b là 2, 1, 3, 4 thì ta có thẻ sử dụng dãy 2 phép
biến đổi T(2) và T(1) để biến đổi a thành b.
Yêu cầu: Cho hai dãy số a, b . Hãy chỉ ra một dãy các phép biến đổi a thành b hoặc
cho biết không tồn tại dãy biến đổi như vậy.
Dữ liệu: Vào từ file văn bản EXARRAY.INP gồm 3 dòng:
• Dòng đầu tiên chứa số nguyên n là số lượng phần tử của mỗi dãy số
(n<=100).
• Dòng thứ hai chứa n số nguyên đôi một khác nhau ứng với các phần tử của
dãy số a.
• Dòng cuối cùng chứa n số nguyên ứng với các phần tử của dãy số b.
Các số trong 2 mảng a và b đều có giá trị nằm trong đoạn [-10000,10000].
Kết quả: ghi ra file văn bản EXARRAY.OUT
41
• Nếu không thể biến đổi a thành b, file chứa số duy nhất số -1.
• Trong trường hợp ngựoc lại, file sẽ gồm 2 dòng:
o Dòng đầu tiên chứa số nguyên k là số lượng phép biến đổi cần áp
dụng.
o Dòng thứ hai chứa k số nguyên duơng i1, i2, …, ik ứng với các phép
biến đổi Ti1, Ti2, …, Tik tìm được.
Hai số liên tiếp trên cùng một dòng được ghi cách nhau bởi một dấu cách.
Ví dụ:
EXARRAY.INP
4
1 3 2 4
2 1 3 4
EXARRAY.OUT
2
2 1
EXARRAY.INP
1
5
2
EXARRAY.OUT
-1
Lời giải:
Thuật toán của ta đơn giản như sau: tìm b[1] trong dãy a rồi di chuyển lên đầu dãy a,
sau đó lại tìm b[2] rồi di chuyển lên vị trí thứ hai của dãy a, ... Nếu tại một bước ta
không tìm thấy số tương ứng trong dãy a thì in ra -1.
Đoạn chương trình sau thể hiện thuật toán:
for i:=1 to n do {duyệt qua tất cả các phần tử của dãy b}
begin
vt[i]:=0; {vt[i] là vị trí xuất hiện của b[i] trong dãy a}
for j:=i to n do {duyệt qua các phần tử của dãy a để tìm số b[i]}
if (a[j]=b[i]) then {tìm đặt b[i]}
begin
vt[i]:=j; {cập nhật lại vt[i]}
break; {thoát khỏi vòng lặp}
end;
if (vt[i]<>0) then {nếu tìm được số b[i]}
begin
kq:=kq+vt[i]-i; {cần thực hiện thêm vt[i]-i phép biển đổi
để đưa số từ vị trí vt[i] về vị trí i}
42
for j:=vt[i]-1 downto i do trao(j) {thực hiện biến đổi!}
end
else
begin {nếu không tìm được số b[i]}
writeln(-1); {in ra -1}
exit; {thoát khỏi thủ tục}
end;
end;
Chương trình:
const MAXN=102;
finp='exarray.inp';
fout='exarray.out';
var n: longint;
a,b:array[1..MAXN] of longint;
procedure nhap;
var i: longint;
begin
readln(n);
for i:=1 to n do read(a[i]);
for i:=1 to n do read(b[i]);
end;
var kq: longint;
vt: array[1..MAXN] of longint;
procedure trao(i: longint);
var tam: longint;
begin
tam:=a[i]; a[i]:=a[i+1]; a[i+1]:=tam;
end;
procedure xuly;
var i, j, tam: longint;
begin
for i:=1 to n do
begin
vt[i]:=0;
for j:=i to n do
if (a[j]=b[i]) then
begin
vt[i]:=j;
break;
end;
if (vt[i]<>0) then
begin
kq:=kq+vt[i]-i;
43
for j:=vt[i]-1 downto i do trao(j)
end
else
begin
writeln(-1);
exit;
end;
end;
writeln(kq);
for i:=1 to n do
for j:=vt[i]-1 downto i do
write(j,' ');
end;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
nhap;
xuly;
close(input);
close(output);
end.
Bài 3: Biến đổi bảng
Xét bảng vuông gồm n dòng và n cột. Các dòng được đánh số từ 1 đến n từ trên
xuống dưới. Các cột được đánh số từ 1 đến n từ trái sang phải. Ô nằm ở vị trí dòng i
và cột j của bảng được gọi là ô (i,j). Trên bảng A đã cho, khoảng cách từ ô (i,j) đến ô
(p,q) được tính bằng |i-p|+|j-q|. Tại ô (i,j) của bảng A ghi số nguyên không âm aij,
i=1,2,…,n; j=1,2,..,n. Dựa vào các số được ghi trên bảng A, người ta cần xây dựng
một bảng B cùng kích thước với A mà trên đó ô (i,j) của bảng B sẽ được ghi số bij
xác định như sau:
• Nếu aij > 0 thì bij = aij
• Nếu aij = 0 thì bij có giá trị bằng giá trị apq của ô (p,q) gần ô (i,j) nhất trong số
các ô có giá trị khác không trên dòng i và cột j của bảng A. Trong truờng hợp
có nhiều ô khác không có cùng khoảng cách nhỏ nhất đến (i,j) thì ô (p,q)
đựoc chọn là ô chứa số lớn nhất trong chúng. Nếu tất cả các phần tử của
dòng i và cột j đều có giá trị 0 thì bij = 0.
Yêu cầu: cho bảng A, hãy tìm bảng B.
Dữ liệu: vào từ file văn bản NZTABLE.INP gồm:
44
• Dòng đầu tiên ghi số nguyên dương n (n ≤ 50)
• Dòng thứ i trong số n dòng tiếp theo ghi n số nguyên không âm ai1, ai2, …, ain
là các số trên dòng thứ i của bảng, i=1,2,…,n; aij ≤ 10000.
Kết quả: đưa ra file văn bản NZTABLE.OUT gồm n dòng, dòng thứ i ghi n số nguyên
dương bi1, bi2, …, bin là các số trên dòng thứ i của bảng B.
Hai số liên tiếp trên cùng một dòng được ghi cách nhau bởi một dấu cách.
Ví dụ:
NZTABLE.INP
4
1 0 3 0
4 0 0 5
0 0 6 0
0 0 0 0
NZTABLE.OUT
1 3 3 5
4 4 6 5
4 6 6 6
4 0 6 5
Lời giải:
Ta xây dựng mảng b giống như định nghĩa của đề bài như đoạn chương trình sau:
if (a[i,j]>0) then b:=a[i,j] else {biển b chỉ phần tử bij. Nếu aij > 0 thì bij = aij}
begin
b:=-maxlongint; {gán bij=-∞ vì ta cần giá trị bij lớn nhất}
kc:=maxlongint; {kc là biến lưu khoảng cách, gán bằng ∞
vì ta cần giá trị nhỏ nhất}
for p:=1 to n do {duyệt qua các ô trên cột j}
if (a[p,j]<>0) then {chỉ xét các ô có giá trị khác 0}
if ((abs(i-p)<kc) or {nếu ô có khoảng cách gần hơn}
((abs(i-p)=kc) and (a[p,j]>b))) {có khoảng cách bằng nhưng giá trị lớn hơn}
then
begin
kc:=abs(i-p); {cập nhật lại khoảng cách}
b:=a[p,j]; {cập nhật lại bij}
end;
45
for p:=1 to n do {duyệt qua các ô trên dòng i}
... {thực hiện tương tự}
if (kc=maxlongint) then b:=0; {nếu tất cả các phần tử của dòng i, cột j
đều bằng 0 thì bij=0}
end;
write(b,' '); {in bij ra màn hình}
Chương trình:
const MAXN=55;
finp='NZTABLE.INP';
fout='NZTABLE.OUT';
var i, j, b, n, kc, p: longint;
a: array[1..MAXN, 1..MAXN] of longint;
begin
assign(input, finp);
reset(input);
assign(output, fout);
rewrite(output);
readln(n);
for i:=1 to n do
for j:=1 to n do
read(a[i,j]);
for i:=1 to n do
begin
for j:=1 to n do
begin
if (a[i,j]>0) then b:=a[i,j] else
begin
b:=-maxlongint;
kc:=maxlongint;
for p:=1 to n do
if (a[p,j]<>0) then
if ((abs(i-p)<kc) or ((abs(i-p)=kc) and (a[p,j]>b))) then
begin
kc:=abs(i-p);
b:=a[p,j];
end;
for p:=1 to n do
if (a[i,p]<>0) then
if ((abs(j-p)<kc) or ((abs(j-p)=kc) and (a[i,p]>b))) then
begin
kc:=abs(j-p);
b:=a[i,p];
end;
if (kc=maxlongint) then b:=0;
end;
46
write(b,' ');
end;
writeln;
end;
close(input);
close(output);
end.
Bài 4: Tìm mật khẩu
Việc bảo vệ máy tính của mình để hạn chế người khác thâm nhập vào là một vấn đề
đặt ra cho mọi nguời sử dụng máy tính. Để tăng tính an toàn trong lưu trữ, một nguời
đã quyết định dấu mật khẩu truy cập máy tính của mình vào một xâu T với một quy
ước sao cho khi cần anh ta có thể lấy lại đuợc mật khẩu từ T như sau.
Là một người yêu thích số học anh ta thường chọn mật khẩu P là một số nguyên tố
và đem dấu vào một xâu ký tự T sao cho P chính là số nguyên tố có giá trị lớn nhất
trong số các số nguyên tố tạo được từ các xâu con của T (xâu con của một xâu ký tự
T là một chuỗi liên tiếp các ký tự trong T).
Ví dụ: xâu T=”Test1234#password5426” chứa mật khẩu là 23 vì T chứa các xâu con
ứng với các số nguyên tố 2,3,23 và 5.
Yêu cầu: Cho một xâu ký tự T chiều dài không quá 250 ký tự. Tìm mật khẩu P đã
dấu trong xâu T biết P có giá trị nhỏ hơn 105
. Dữ liệu cho đảm bảo T chứa ít nhất 1
số nguyên tố.
Dữ liệu: Vào từ file văn bản PASSWORD.INP gồm 1 dòng duy nhất là xâu T.
Kết quả: Ghi ra file văn bản PASSWORD.OUT chứa số P tìm được.
Ví dụ:
PASSWORD.INP
Test1234#password5426
PASSWORD.OUT
23
Lời giải:
Ta duyệt qua tất cả các xâu con của xâu T mà có thể tạo thành một số và kiểm tra số
đó có phải số nguyên tố hay không.
Để duyệt qua các xâu con, ta duyệt qua vị trí đầu:
for i:=1 to length(t) do {i là vị trí đầu của xâu con}
Với mỗi vị trí đầu i, ta duyệt qua vị trí cuối của xâu con:
j:=i;
while (j<=length(t)) do
begin
...
inc(j);
end;
Có hai điều kiện để ta dừng quá trình duyệt một xâu con với vị trí đầu là i:
47
• Gặp một ký tự không phải chữ số:
if (t[j]<'1') or (t[j]>'9') then break;
• Số tạo thành lớn hơn hoặc bằng 105
, vì đề bài đã nêu rõ P có giá trị nhỏ hơn
105
:
if v>=100000 then break;
Khi đọc được một chữ số mới, ta nhân số hiện tại với 10 rồi cộng thêm chữ số mới:
v:=v*10+ord(t[j])-ord('0');
Nếu số thu được là số nguyên tố và lớn hơn kết quả tốt nhất tìm được thì cập nhật
kết quả:
if nguyento(v) then
if (v>kq) then kq:=v;
Chương trình:
const finp='password.inp';
fout='password.out';
function nguyento(n: longint): boolean;
var i: longint;
begin
nguyento:=false;
if n<2 then exit;
for i:=2 to trunc(sqrt(n)) do
if (n mod i = 0) then
exit;
nguyento:=true;
end;
var t: string;
i, kq, v, j: longint;
begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);
readln(t);
kq:=-1;
for i:=1 to length(t) do
begin
v:=0;
j:=i;
while (j<=length(t)) do
begin
if (t[j]<'1') or (t[j]>'9') then break;
v:=v*10+ord(t[j])-ord('0');
if v>=100000 then break;
writeln(v);
48
if nguyento(v) then
if (v>kq) then kq:=v;
inc(j);
end;
end;
writeln(kq);
close(input);
close(output);
end.
49

More Related Content

What's hot

Công Thức Lượng GIác
Công Thức Lượng GIácCông Thức Lượng GIác
Công Thức Lượng GIácHà Cao
 
Ngôn ngữ lập trình pascal (bổ trợ tin 11)
Ngôn ngữ lập trình pascal (bổ trợ tin 11)Ngôn ngữ lập trình pascal (bổ trợ tin 11)
Ngôn ngữ lập trình pascal (bổ trợ tin 11)Hong Phuoc Nguyen
 
Phương pháp nhánh cận
Phương pháp nhánh cậnPhương pháp nhánh cận
Phương pháp nhánh cậnDiên Vĩ
 
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.Bùi Việt Hà
 
Bai tap pascal tong hop
Bai tap pascal tong hopBai tap pascal tong hop
Bai tap pascal tong hopQuyen Hong
 
Giao an trinh_pascal_bai_tap_co_dap_an_huong_dan
Giao an trinh_pascal_bai_tap_co_dap_an_huong_danGiao an trinh_pascal_bai_tap_co_dap_an_huong_dan
Giao an trinh_pascal_bai_tap_co_dap_an_huong_danVõ Tâm Long
 
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp án
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp ánĐề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp án
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp ánBồi dưỡng Toán lớp 6
 
Phương pháp số và lập trình - Giải phương trình phi tuyến
Phương pháp số và lập trình - Giải phương trình phi tuyếnPhương pháp số và lập trình - Giải phương trình phi tuyến
Phương pháp số và lập trình - Giải phương trình phi tuyếnHajunior9x
 
Dien tu so dhbk ha noi
Dien tu so   dhbk ha noiDien tu so   dhbk ha noi
Dien tu so dhbk ha noiHung Mobi QL
 
Cac ham va thu tuc trong pascal
Cac ham va thu tuc trong pascalCac ham va thu tuc trong pascal
Cac ham va thu tuc trong pascalVõ Tâm Long
 
Lap trinh matlab_co_ban_1731
Lap trinh matlab_co_ban_1731Lap trinh matlab_co_ban_1731
Lap trinh matlab_co_ban_1731Vu Tuan
 
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70lovestem
 
Slide bài giảng về lập trình Scratch dành cho GV
Slide bài giảng về lập trình Scratch dành cho GVSlide bài giảng về lập trình Scratch dành cho GV
Slide bài giảng về lập trình Scratch dành cho GVBùi Việt Hà
 

What's hot (20)

Công Thức Lượng GIác
Công Thức Lượng GIácCông Thức Lượng GIác
Công Thức Lượng GIác
 
Ngôn ngữ lập trình pascal (bổ trợ tin 11)
Ngôn ngữ lập trình pascal (bổ trợ tin 11)Ngôn ngữ lập trình pascal (bổ trợ tin 11)
Ngôn ngữ lập trình pascal (bổ trợ tin 11)
 
Phương pháp nhánh cận
Phương pháp nhánh cậnPhương pháp nhánh cận
Phương pháp nhánh cận
 
Thuật toán giảm bậc mô hình và ứng dụng cho bài toán điều khiển
Thuật toán giảm bậc mô hình và ứng dụng cho bài toán điều khiểnThuật toán giảm bậc mô hình và ứng dụng cho bài toán điều khiển
Thuật toán giảm bậc mô hình và ứng dụng cho bài toán điều khiển
 
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.
Unit 6 nâng cao. Bài 6. Xử lý số và xâu ký tự.
 
Bai tap pascal tong hop
Bai tap pascal tong hopBai tap pascal tong hop
Bai tap pascal tong hop
 
ĐỀ VÀ ĐÁP ÁN TOÁN + TV LỚP 2 THEO TT22
ĐỀ VÀ ĐÁP ÁN TOÁN + TV LỚP 2 THEO TT22ĐỀ VÀ ĐÁP ÁN TOÁN + TV LỚP 2 THEO TT22
ĐỀ VÀ ĐÁP ÁN TOÁN + TV LỚP 2 THEO TT22
 
Simpson
SimpsonSimpson
Simpson
 
Giao an trinh_pascal_bai_tap_co_dap_an_huong_dan
Giao an trinh_pascal_bai_tap_co_dap_an_huong_danGiao an trinh_pascal_bai_tap_co_dap_an_huong_dan
Giao an trinh_pascal_bai_tap_co_dap_an_huong_dan
 
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp án
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp ánĐề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp án
Đề thi vào lớp 10 môn Toán của Hà Nội năm học 2019 - 2020 có đáp án
 
Phương pháp số và lập trình - Giải phương trình phi tuyến
Phương pháp số và lập trình - Giải phương trình phi tuyếnPhương pháp số và lập trình - Giải phương trình phi tuyến
Phương pháp số và lập trình - Giải phương trình phi tuyến
 
Dien tu so dhbk ha noi
Dien tu so   dhbk ha noiDien tu so   dhbk ha noi
Dien tu so dhbk ha noi
 
Cac ham va thu tuc trong pascal
Cac ham va thu tuc trong pascalCac ham va thu tuc trong pascal
Cac ham va thu tuc trong pascal
 
Cách làm báo cáo thực tập đại học sư phạm kỹ thuật điểm cao
Cách làm báo cáo thực tập đại học sư phạm kỹ thuật điểm caoCách làm báo cáo thực tập đại học sư phạm kỹ thuật điểm cao
Cách làm báo cáo thực tập đại học sư phạm kỹ thuật điểm cao
 
Lap trinh matlab_co_ban_1731
Lap trinh matlab_co_ban_1731Lap trinh matlab_co_ban_1731
Lap trinh matlab_co_ban_1731
 
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70
Số phức-6-Bài toán GTNN GTLN trên tập số phức-pages 63-70
 
Slide bài giảng về lập trình Scratch dành cho GV
Slide bài giảng về lập trình Scratch dành cho GVSlide bài giảng về lập trình Scratch dành cho GV
Slide bài giảng về lập trình Scratch dành cho GV
 
Cay ion
Cay ionCay ion
Cay ion
 
10 ĐỀ THI GIỮA HỌC KÌ 2 MÔN TOÁN LỚP 2
10 ĐỀ THI GIỮA HỌC KÌ 2 MÔN TOÁN LỚP 210 ĐỀ THI GIỮA HỌC KÌ 2 MÔN TOÁN LỚP 2
10 ĐỀ THI GIỮA HỌC KÌ 2 MÔN TOÁN LỚP 2
 
Huong dan thiet ke thi nghiem ao
Huong dan thiet ke thi nghiem aoHuong dan thiet ke thi nghiem ao
Huong dan thiet ke thi nghiem ao
 

Viewers also liked

Chuong 4 tin 11
Chuong 4 tin 11Chuong 4 tin 11
Chuong 4 tin 11Sunkute
 
Bai tap pascal co giai
Bai tap pascal co giaiBai tap pascal co giai
Bai tap pascal co giaitrungdha
 
Characteristics of narration
Characteristics of  narrationCharacteristics of  narration
Characteristics of narrationphoebinku
 
Why Can't We Be Friends?
Why Can't We Be Friends?Why Can't We Be Friends?
Why Can't We Be Friends?Snag
 
Grm 201 project
Grm 201 projectGrm 201 project
Grm 201 projectnmjameson
 
Introduction to Ext JS 4
Introduction to Ext JS 4Introduction to Ext JS 4
Introduction to Ext JS 4Stefan Gehrig
 
Acerca de la geometria de lobachevski a. s. smogorzhevski
Acerca de la geometria de lobachevski   a. s. smogorzhevskiAcerca de la geometria de lobachevski   a. s. smogorzhevski
Acerca de la geometria de lobachevski a. s. smogorzhevskipedro dowling
 
Rian vebrianto brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...
Rian vebrianto  brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...Rian vebrianto  brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...
Rian vebrianto brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...Rian vebrianto
 
PHP Apps on the Move - Migrating from In-House to Cloud
PHP Apps on the Move - Migrating from In-House to Cloud  PHP Apps on the Move - Migrating from In-House to Cloud
PHP Apps on the Move - Migrating from In-House to Cloud RightScale
 
AmyandSusan
AmyandSusanAmyandSusan
AmyandSusansgrobins
 
Randall Santos - Portfolio 2008
Randall Santos - Portfolio 2008Randall Santos - Portfolio 2008
Randall Santos - Portfolio 2008randallsan
 
蘭花草歌
蘭花草歌蘭花草歌
蘭花草歌Ryan Wong
 
Horizons Beyond Dreams Business Plan
Horizons Beyond Dreams Business PlanHorizons Beyond Dreams Business Plan
Horizons Beyond Dreams Business Planphillipcfrankis
 
The history of video games goes as far back as the early 1940s
The history of video games goes as far back as the early 1940sThe history of video games goes as far back as the early 1940s
The history of video games goes as far back as the early 1940sJian Li
 
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.Rakesh Kumar
 

Viewers also liked (17)

Chuong 4 tin 11
Chuong 4 tin 11Chuong 4 tin 11
Chuong 4 tin 11
 
Bai tap pascal co giai
Bai tap pascal co giaiBai tap pascal co giai
Bai tap pascal co giai
 
Characteristics of narration
Characteristics of  narrationCharacteristics of  narration
Characteristics of narration
 
Why Can't We Be Friends?
Why Can't We Be Friends?Why Can't We Be Friends?
Why Can't We Be Friends?
 
Grm 201 project
Grm 201 projectGrm 201 project
Grm 201 project
 
East Grage Project
East Grage ProjectEast Grage Project
East Grage Project
 
Introduction to Ext JS 4
Introduction to Ext JS 4Introduction to Ext JS 4
Introduction to Ext JS 4
 
Acerca de la geometria de lobachevski a. s. smogorzhevski
Acerca de la geometria de lobachevski   a. s. smogorzhevskiAcerca de la geometria de lobachevski   a. s. smogorzhevski
Acerca de la geometria de lobachevski a. s. smogorzhevski
 
Rian vebrianto brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...
Rian vebrianto  brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...Rian vebrianto  brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...
Rian vebrianto brunai (PEMBANGUNAN MEDIA PENGAJARAN: MODUL DAN MULTIMEDIA DA...
 
PHP Apps on the Move - Migrating from In-House to Cloud
PHP Apps on the Move - Migrating from In-House to Cloud  PHP Apps on the Move - Migrating from In-House to Cloud
PHP Apps on the Move - Migrating from In-House to Cloud
 
AmyandSusan
AmyandSusanAmyandSusan
AmyandSusan
 
Randall Santos - Portfolio 2008
Randall Santos - Portfolio 2008Randall Santos - Portfolio 2008
Randall Santos - Portfolio 2008
 
Bebepolis
BebepolisBebepolis
Bebepolis
 
蘭花草歌
蘭花草歌蘭花草歌
蘭花草歌
 
Horizons Beyond Dreams Business Plan
Horizons Beyond Dreams Business PlanHorizons Beyond Dreams Business Plan
Horizons Beyond Dreams Business Plan
 
The history of video games goes as far back as the early 1940s
The history of video games goes as far back as the early 1940sThe history of video games goes as far back as the early 1940s
The history of video games goes as far back as the early 1940s
 
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.
Zed-Sales™ - a flagship product of Zed-Axis Technologies Pvt. Ltd.
 

Similar to TinHoc_tuyentapde_nk

bài tập cấu trúc dữ liệu 1
bài tập cấu trúc dữ liệu 1bài tập cấu trúc dữ liệu 1
bài tập cấu trúc dữ liệu 1NguynMinh294
 
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdf
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdfchuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdf
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdfcholacha
 
bài tập cấu trúc dữ liệu 4
bài tập cấu trúc dữ liệu 4bài tập cấu trúc dữ liệu 4
bài tập cấu trúc dữ liệu 4NguynMinh294
 
bài tập cấu trúc dữ liệu 3
bài tập cấu trúc dữ liệu 3bài tập cấu trúc dữ liệu 3
bài tập cấu trúc dữ liệu 3NguynMinh294
 
Chuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfChuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfHngTrn365275
 
Chuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfChuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfnguyenkaka2
 
bài tập cấu trúc dữ liệu 2
bài tập cấu trúc dữ liệu 2bài tập cấu trúc dữ liệu 2
bài tập cấu trúc dữ liệu 2NguynMinh294
 
Bo de on luyen hsg tin hoc
Bo de on luyen hsg tin hocBo de on luyen hsg tin hoc
Bo de on luyen hsg tin hocVo Van Phuc
 
Phân tích một số thuật toán
Phân tích một số thuật toánPhân tích một số thuật toán
Phân tích một số thuật toánHồ Lợi
 
bài tập cấu trúc dữ liệu 5
bài tập cấu trúc dữ liệu 5bài tập cấu trúc dữ liệu 5
bài tập cấu trúc dữ liệu 5NguynMinh294
 
Giáo trình kỹ thuật số chương 1-2.doc
Giáo trình kỹ thuật số chương 1-2.docGiáo trình kỹ thuật số chương 1-2.doc
Giáo trình kỹ thuật số chương 1-2.docMan_Ebook
 
Dientuso Sld
Dientuso SldDientuso Sld
Dientuso Sldhoadktd
 
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptx
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptxBoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptx
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptxHongNgcCnh2
 
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...Nguyen Thanh Tu Collection
 
H hai epc_baitap
H hai epc_baitapH hai epc_baitap
H hai epc_baitapHồ Lợi
 
bài tập cấu trúc dữ liệu 7
bài tập cấu trúc dữ liệu 7bài tập cấu trúc dữ liệu 7
bài tập cấu trúc dữ liệu 7NguynMinh294
 

Similar to TinHoc_tuyentapde_nk (20)

bài tập cấu trúc dữ liệu 1
bài tập cấu trúc dữ liệu 1bài tập cấu trúc dữ liệu 1
bài tập cấu trúc dữ liệu 1
 
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdf
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdfchuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdf
chuyen-de-day-so-cap-so-cong-va-cap-so-nhan-toan-11-canh-dieu.pdf
 
bài tập cấu trúc dữ liệu 4
bài tập cấu trúc dữ liệu 4bài tập cấu trúc dữ liệu 4
bài tập cấu trúc dữ liệu 4
 
bài tập cấu trúc dữ liệu 3
bài tập cấu trúc dữ liệu 3bài tập cấu trúc dữ liệu 3
bài tập cấu trúc dữ liệu 3
 
Chuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfChuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdf
 
Chuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdfChuong 1 Matlab co ban.pdf
Chuong 1 Matlab co ban.pdf
 
bài tập cấu trúc dữ liệu 2
bài tập cấu trúc dữ liệu 2bài tập cấu trúc dữ liệu 2
bài tập cấu trúc dữ liệu 2
 
Bo de on luyen hsg tin hoc
Bo de on luyen hsg tin hocBo de on luyen hsg tin hoc
Bo de on luyen hsg tin hoc
 
Phân tích một số thuật toán
Phân tích một số thuật toánPhân tích một số thuật toán
Phân tích một số thuật toán
 
bài tập cấu trúc dữ liệu 5
bài tập cấu trúc dữ liệu 5bài tập cấu trúc dữ liệu 5
bài tập cấu trúc dữ liệu 5
 
Giáo trình kỹ thuật số chương 1-2.doc
Giáo trình kỹ thuật số chương 1-2.docGiáo trình kỹ thuật số chương 1-2.doc
Giáo trình kỹ thuật số chương 1-2.doc
 
Dientuso Sld
Dientuso SldDientuso Sld
Dientuso Sld
 
Danhsach baitap
Danhsach baitapDanhsach baitap
Danhsach baitap
 
De17
De17De17
De17
 
Baitap ktlt
Baitap ktltBaitap ktlt
Baitap ktlt
 
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptx
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptxBoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptx
BoiDuongHSGTin_DuyetToanBoVaCacPhuongPhapCaiTien.pptx
 
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...
BÀI TẬP DẠY THÊM TOÁN 11 - SÁCH CHÂN TRỜI SÁNG TẠO - CẢ NĂM - CHUYÊN ĐỀ 2 - D...
 
H hai epc_baitap
H hai epc_baitapH hai epc_baitap
H hai epc_baitap
 
Bai11
Bai11Bai11
Bai11
 
bài tập cấu trúc dữ liệu 7
bài tập cấu trúc dữ liệu 7bài tập cấu trúc dữ liệu 7
bài tập cấu trúc dữ liệu 7
 

TinHoc_tuyentapde_nk

  • 1. Năm học 2002-2003 Bài 1: CHỮ SỐ tên file chương trình CHUSO.PAS Xét dãy số tự nhiên {an} đuợc xây dựng theo quy tắc sau: • Cho trước số a0 là một số tự nhiên có tối đa 10 chữ số. • Số ai (i>0) là một số tự nhiên nhận được từ ai-1 bằng cách viết thêm vào sau các chữ số của ai-1 chính ai-1 nhưng viết theo thứ tự ngược lại. Ví dụ: Với a0 = 123 thì a1 = 123321, a2 = 123321123321, a3 = 123321123321123321123321 Với hai số N và M cho trước, hãy tìm chữ số thứ M trong aN. Dữ liệu cho trong file văn bản với tên là CHUSO.INP trong đó dòng đầu chứa số a0, dòng thứ hai chứa hai số N và M. Kết quả ghi ra file văn bản với tên là CHUSO.OUT. Trong trường hợp có lời giải, file này sẽ chứa số tìm được, ngược lại file này chứa số -1. Ví dụ: CHUSO.INP 123 3 7 Giới hạn: 1 ≤ N ≤ 25, 1 ≤ M ≤ 1 000 000 000. Lời giải: Trước tiên ta nhận xét mặc dù đề bài cho a0 là số tự nhiên, nhưng vì bài toán không sử dụng tính chất của số nên ta có thể xem a0 như một xâu ký tự. Gọi L là số ký tự của a0, ta thấy a0 có L ký tự, a1 có 2L ký tự, a2 có 4L ký tự, ..., aN có 2N L ký tự. Ký hiệu sR là chuỗi ký tự đảo ngược của chuỗi s. Ví dụ: nếu s="abca" thì sR ="acba", các chữ cái được viết theo thứ tự ngược lại. Để ý là với hai chuỗi a, b bất kỳ ta có (ab)R =bR aR . Theo đề bài, ta có a1 = a0a0 R , a2=a1a1 R =a0a0 R (a0a0 R )R =a0a0 R a0a0 R , ..., aN= a0a0 R a0a0 R ... a0a0 R , nghĩa là xâu aN sẽ ghép thành từ các xâu a0 và a0 R xen kẽ nhau. Từ nhận xét này ta có thuật toán sau: Nếu vị trí M nằm ngoài xâu ký tự aN, hay nói cách khác nếu M < 1 hoặc M > 2N L thì in ra -1. Đoạn lệnh sau đây thể hiện điều này: if (m<1) or (m>l*(1 shl n)) then begin {chú ý: 1 shl n = 2^n} timchuso:=-1; exit; end; Còn nếu không, ta xem vị trí M sẽ thuộc về một xâu a0 hay một xâu đảo ngược a0 R , điều này được chỉ ra bởi giá trị của biểu thức ((M-1) div L) mod 2 bằng 0 hay 1. Biết được điều này, sử dụng phép lấy số dư, ta sẽ tìm được vị trí của ký tự cần tìm trong chuỗi a0: i:=(m-1) mod l+1; {i là vị trí tương ứng với vị trí M trong chuỗi a0} if ((m-1) div l) mod 2=1 then i:=l-i+1; {nếu vị trí M thuộc xâu đảo ngược a0 R thì ta đảo ngược vị trí i} timchuso:=ord(a0[i])-ord('0'); {kết quả bằng chữ số a0[i]} 1 CHUSO.OUT 1
  • 2. Chương trình: const finp='chuso.inp'; fout='chuso.out'; var a0: string; n, m: longint; function timchuso(n,m: longint): longint; var l, i: longint; begin l:=length(a0); if (m<1) or (m>l*(1 shl n)) then begin timchuso:=-1; exit; end; i:=(m-1) mod l+1; if ((m-1) div l) mod 2=1 then i:=l-i+1; timchuso:=ord(a0[i])-ord('0'); end; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); readln(a0); readln(n,m); write(timchuso(n,m)); close(input); close(output); end. Bài 2: TÍNH DIỆN TÍCH tên file chương trình HCN.PAS Trên mặt phẳng tọa độ cho N (N ≤ 10 000) hình chữ nhật với các cạnh song song với các trục tọa độ. Các hình chữ nhật được đánh số từ 1 tới N. Hình chữ nhật thứ i được cho bởi toạ độ đỉnh trái dưới (xi1 , yi1) và tọa độ đỉnh phải trên (xi2, yi2). Các số xi1 , yi1, xi2, yi2 là các số nguyên trong phạm vi từ -100 đến 100. Hãy lập trình tính: 1. Diện tích của phần mặt phẳng mà N hình chữ nhật này phủ. 2. Tính diện tích phần chung của N hình chữ nhật này. Dữ liệu cho trong file HCN.INP trong đó dòng đầu chứa số N. Dòng thứ i trong N dòng tiếp theo chứa 4 số số xi1 , yi1, xi2, yi2. Kết quả ghi ra file HCN.OUT gồm 2 dòng, trong đó dòng đầu chứa số S1 là kết quả của câu 1. Dòng thứ hai chứa số S2 là kết quả của câu 2. 2
  • 3. Ví dụ: HCN.INP 2 0 0 1 1 -1 -1 1 1 3 HCN.OUT 4 1
  • 4. Bài 3: BẢNG QUẢNG CÁO Tên file chương trình QUANGCAO.PAS Trên quảng trường trung tâm của thủ đô Rome có một bảng quảng cáo hình chữ nhật gồm N x M ô vuông. Mỗi ô có một bóng đèn, mỗi bóng đèn có hai trạng thái tắt hoặc sáng. Ứng với mỗi dòng cũng như mỗi cột có một công tắc. Khi tác động đến một công tắc nào đó tất cả các bóng đèn trên dòng hoặc cột tương ứng sẽ đổi sang trạng thái ngược lại (đang sáng thành tắc, đang tắc được bật sáng). Để mừng đội nhà thắng trận trong trận cầu chiều qua người phụ trách bảng quảng cáo muốn bảng có được nhiều bóng đèn sáng nhất.Với trạng thái bảng quảng cáo hiện thời cho trước, người phụ trách nhờ bạn lập trình tìm một phương án tác động lên các công tắc để nhận được trạng thái bảng quảng cáo mong muốn. Bạn hãy giúp nhà phụ trách thực hiện điều đó. Dữ liệu cho trong file văn bản với tên là QUANGCAO.INP trong đó: • Dòng đầu chứa hai số N và M (: 1 ≤ N ≤ 10, 1 ≤ M ≤ 100). • Dòng thứ i trong N dòng tiếp theo chứa M số 0 hoặc 1. Số thứ j cho biết trạng thái của bóng đèn thứ j trên dòng thứ i của bảng (1 tương ứng với bóng đèn sáng, 0 tương ứng với bóng đèn tắt). Kết quả ghi ra trong file QUANGCAO.OUT trong đó: • Dòng đầu là số bóng đèn sáng trên bảng tìm được • Dòng thứ hai chứa S là số lần bạn tác động lên các công tắc. • S dòng tiếp theo lần lượt ghi ra S công tắc theo trình tự cần bật. Dòng thứ j trong S dòng này chứa một xâu độ dài không quá 4, ký tự đầu là ‘D’ hoặc ‘C’ tương ứng với tác động thứ i là lên dòng hay cột. Phần còn lại của xâu là chỉ số của dòng hay cột tương ứng. Ví dụ: QUANGCAO.INP QUANGCAO.OUT 4 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 16 4 C1 C4 D1 D4 Lời giải: Trước hết ta đưa ra hai nhận xét: • Mỗi công tắc chỉ cần được tác động nhiều nhất một lần: thật vậy, tác động một công tắc 2 lần sẽ cho kết quả giống như khi không tác động lên công tắc. • Thứ tự tác động lên các công tắc là không quan trọng. Nói cách khác, tác động một dãy công tắc theo trình tự nào cũng mang lại kết quả như nhau. Từ hai nhận xét trên, ta thấy mỗi công tắc sẽ có hai khả năng: tác động hoặc không tác động. Có tất cả M+N công tắc, vậy số khả năng là 2M+N . Theo giới hạn đề bài ra, con số này có thể rất lớn. Tuy nhiên để tìm số bóng đèn sáng nhiều nhất, ta chỉ cần 4
  • 5. duyệt qua 2N ≤ 210 = 1024 khả năng tác động lên các công tắc trên các hàng của bảng. Với mỗi khả năng này, đối với mỗi cột ta khẳng định được ngay có cần phải tác động lên công tắc của cột đó hay không: nếu tác động lên công tắc cột mà số đèn sáng trên cột đó nhiều hơn thì ta sẽ tác động. Đoạn lệnh sau đây thể hiện điều này. Chú ý trong chương trình dưới đây, ta đánh số cột, hàng bắt đầu từ 0. for j:=0 to m-1 do begin dem:=0; {đếm số đèn sáng trên cột j} for i:=0 to n-1 do if (b[i,j]=1) then inc(dem); if (dem<n-dem) then {tác động lên công tắc sẽ cho n-dem bóng đèn sáng} begin {nếu tác động lên công tắc có lợi hơn:} batcot(b,j); {tác động!} inc(t); {tăng biến ghi nhận số lần tác động lên một} cot[j]:=true; {ghi nhớ rằng đã tác động lên cột j} end; end; Trong 2N khả năng này, ta sẽ chọn ra khả năng cho số bóng đèn sáng nhiều nhất. Về cài đặt chương trình, chú ý sử dụng kỹ thuật xử lý bit khi duyệt qua 2N khả năng tác động lên các công tắc trên hàng. Dùng kỹ thuật này, chương trình sẽ được viết nhanh và gọn hơn. Câu lệnh sau đây thực hiện điều này: for s:=0 to (1 shl n)-1 do {biến s là một dãy N bit thể hiện một khả năng} Do ta muốn có một dãy N bit đến biến s sẽ chạy từ 0 đến 2N - 1. Để kiểm tra trong khả năng s, một công tắc nào đó có được tác động hay không ta dùng thủ tục kiểm tra một bit có được bật hay không: function bitbat(s, i: longint): boolean; begin bitbat:=((s shr i) and 1)=1; end; Chương trình: const finp='quangcao.inp'; fout='quangcao.out'; maxn=15; maxm=110; type bang=array[0..maxn-1, 0..maxm-1] of byte; var n, m, kq, sotacdong: longint; a, b: bang; hang, luuhang: array[0..MAXN-1] of boolean; cot, luucot: array[0..MAXM-1] of boolean; procedure nhap; 5
  • 6. var i, j: longint; begin readln(n,m); for i:=0 to n-1 do for j:=0 to m-1 do read(a[i,j]); end; function bitbat(s, i: longint): boolean; begin bitbat:=((s shr i) and 1)=1; end; procedure batcot(var a: bang; t: longint); var i: longint; begin for i:=0 to n-1 do a[i,t]:=1-a[i,t]; end; procedure bathang(var a: bang; t: longint); var i: longint; begin for i:=0 to m-1 do a[t,i]:=1-a[t,i]; end; function sodensang(var a: bang): longint; var i,j,dem: longint; begin dem:=0; for i:=0 to n-1 do for j:=0 to m-1 do if (a[i,j]=1) then inc(dem); sodensang:=dem; end; procedure xuly; var s,i,j,dem,k,t: longint; begin kq:=0; for s:=0 to (1 shl n)-1 do begin b:=a; t:=0; fillchar(hang,sizeof(hang),false); fillchar(cot,sizeof(cot),false); for i:=0 to n-1 do if (bitbat(s,i)) then begin bathang(b,i); inc(t); hang[i]:=true; end; for j:=0 to m-1 do begin dem:=0; for i:=0 to n-1 do if (b[i,j]=1) then inc(dem); 6
  • 7. if (dem<n-dem) then begin batcot(b,j); inc(t); cot[j]:=true; end; end; k:=sodensang(b); if (k>kq) then begin kq:=k; sotacdong:=t; luuhang:=hang; luucot:=cot; end; end; end; procedure xuat; var i: longint; begin writeln(kq); writeln(sotacdong); for i:=0 to m-1 do if (luucot[i]) then writeln('C',i+1); for i:=0 to n-1 do if (luuhang[i]) then writeln('D',i+1); end; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); nhap; xuly; xuat; close(input); close(output); end. Năm học 2003-2004 Bài 1: TỔNG LỚN NHẤT tên chương trình: SUM.PAS Cho một bảng A gồm N x N số nguyên (N ≤ 100), các dòng được đánh số trên xuống dưới bắt đầu từ 1, các cột được đánh số từ trái qua phải cũng bắt đầu từ 1. Mỗi số trong bảng có giá trị tuyệt đối không vượt quá 10000. Đường chéo chính của bảng là đường thẳng nối hai ô (1,1) và (N,N). Như vậy trên bảng có 2N-1 đuờng chéo song song với đường chéo chính. Bài toán: Hãy tìm đường chéo song song với đường chéo chính có tổng các phần tử trên đường chéo đó là lớn nhất. Dữ liệu vào cho trong file văn bản SUM.INP trong đó: 7
  • 8. Dòng đầu chứa số N. Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các phần tử nằm trên dòng thứ i của bảng A. Kết quả ghi ra trong file văn bản SUM.OUT trong đó chứa một số nguyên duy nhất là tổng các phần tử trên đường chéo mà bạn tìm được. 1 2 4 3 3 4 2 5 2 5 4 3 4 3 2 5 Ví dụ: với bảng A như hình vẽ, đường chéo chính chính là đường chéo có tổng lớn nhất (bằng 14), các file dữ liệu vào/ra lần lượt có nội dung như sau: SUM.INP SUM.OUT 4 1 2 4 3 3 4 2 5 2 5 4 3 4 3 2 5 14 Lời giải Để giải bài toán này ta cần biết cách duyệt qua các đường chéo song song với đường chéo chính của bảng số một cách nhanh gọn và hiệu quả. Có rất nhiều cách để thực hiện điều này. Sau đây là một cách: để ý rằng trên một đường chéo, hiệu giữa chỉ số hàng và chỉ số cột là không đổi. Hiệu này nhận giá trị từ 1-N (tương ứng với đường chéo gồm 1 ô góc trên phải (1,N)) cho đến N-1 (tương ứng với đường chéo gồm 1 ô góc dưới trái (N,1)). Do đó ta lần lượt xét các giá trị hiệu từ 1-N đến N- 1. Ký hiệu giá trị này là T. Với mỗi giá trị T, ta duyệt qua các cột trên đường chéo tương ứng. Nếu T≥0 (tương ứng với các đường chéo ở nửa dưới của bảng) thì cột bắt đầu là 1 còn nếu T<0 thì cột bắt đầu là 1-T. Đoạn lệnh sau thể hiện cách làm này: for t:=1-n to n-1 do if (t>=0) then j:=1 else j:=1-t; {j là biến lưu cột bắt đầu của đường chéo} s:=0; {tổng các phần tử trên đường chéo} repeat s:=s+a[j+t,j]; {cộng giá trị ô hiện thời vào tổng} inc(j); {sang cột mới} until (j+t>n) or (j>n); {vượt quá phạm vi của bảng} if (s>kq) then kq:=s; {cập nhật kết quả} end; Chương trình: 8 Đường chéo
  • 9. const MAXN=105; finp='sum.inp'; fout='sum.out'; var n, i, j, t, s, kq: longint; a: array[1..MAXN, 1..MAXN] of longint; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); readln(n); for i:=1 to n do for j:=1 to n do read(a[i,j]); kq:=-maxlongint; for t:=1-n to n-1 do begin if (t>=0) then j:=1 else j:=1-t; s:=0; repeat s:=s+a[j+t,j]; inc(j); until (j+t>n) or (j>n); if (s>kq) then kq:=s; end; writeln(kq); close(input); close(output); end. Bài 2: SẮP XẾP Tên chương trình: SORT.PAS Cho một dãy X gồm N số nguyên trong phạm vi từ -10000 đến 10000 (1 ≤N ≤ 100000). 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ứ i trong N dòng tiếp theo chứa số thứ i trong dãy X. 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 mỗi số trên một dòng. Ví dụ: SORT.INP SORT.OUT 4 3 4 2 5 4 3 2 9
  • 10. 5 Lời giải: Phương pháp sắp xếp mà chúng ta dùng là sắp xếp đếm (counting sort). Phương pháp này tận dụng việc giới hạn của các số cần sắp xếp có thể lưu đủ trong bộ nhớ, trong bài toán này phạm vi các số là -10000..10000. Ta sẽ dùng mảng dem[- 10000..10000] trong đó dem[x] lưu số lần xuất hiện của số x trong dãy số. Bài toán này được ra với yêu cầu làm trên trình biên dịch Borland Pascal. Với trình biên dịch cũ này, quản lý bộ nhớ là một điều quan trọng do dung lượng bộ nhớ bị hạn chế. Chúng tôi sẽ trình bày lời giải bài toán này trên cơ sở bộ nhớ hạn chế đó. Do mỗi số có thể xuất hiện đến N ≤ 100 000 lần nên mảng dem phải mang kiểu dữ liệu longint (số nguyên 32 bit) trong Pascal. Trong Borland Pascal, khi khai báo mảng 20000 phần tử longint sẽ bị báo lỗi là không đủ bộ nhớ. Có một cách để giải quyết điều này: Khai bảo mảng dem với kiểu dữ liệu word (có giới hạn từ 0..65535) như sau: var dem: array[-10000..10000] of word; Ta nhận xét chỉ có nhiều nhất một phần tử của mang dem có thể có giá trị lớn hơn hoặc bằng 60000, vì tổng số lượng số nhiều nhất là 100 000. Do đó ta quản lý thêm một biến lưu phần tử đặc biệt có giá trị đếm vượt 60000 này (nếu có). Ta đặt tên biến này là v. Đoạn lệnh dưới đây đọc vào các số và quản lý dữ liệu: for i:=1 to n do begin readln(x); {đọc vào một số x} inc(dem[x]); {tăng biến đếm số lần xuất hiện của số x} if (dem[x]=60000) then {nếu đã có 60000 số x xuất hiện} begin v:=x; {lưu lại số x duy nhất này} dem[x]:=0; {gán lại biến đếm bằng 0 để tránh tràn số} end; end; Đoạn lệnh dưới đây in ra các số đã sắp xếp theo thứ tự giảm dần: for i:=-10000 downto 10000 do {duyệt qua phạm vi của các số: [-10000,10000]) begin for j:=1 to dem[i] do {in ra số i với số lần là dem[i]} writeln(i); if (v=i) then for j:=1 to 60000 do {nếu i là số đặc biệt thì ta cần in thêm 60000 lần xuất hiện nữa} writeln(i); 10
  • 11. end; Chương trình: const finp='sort.inp'; fout='sort.out'; var dem: array[-10000..10000] of word; n, i, v: longint; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); fillchar(dem,sizeof(dem),0); readln(n); v:=0; for i:=1 to n do begin readln(x); inc(dem[x]); if (dem[x]=60000) then begin v:=x; dem[x]:=0; end; end; for i:=-10000 downto 10000 do begin for j:=1 to dem[i] do writeln(i); if (v=i) then for j:=1 to 60000 do writeln(i); end; close(input); close(output); end. Bài 3: HÌNH VUÔNG tên chưong trình: SQUARE.PAS Trên mặt phẳng cho N hình vuông với các cạnh song song với hệ trục toạ độ được đánh số từ 1 đến N (1 ≤N ≤2000). Hình vuông thứ i được cho bởi toạ độ góc dưới trái (xi, yi) và toạ độ đỉnh phải trên là (zi, ti). Toạ độ của các đỉnh là các số nguyên trong phạm vi -10000 đến 10000. Khoảng cách giữa hai hình vuông A và B được định nghĩa là độ dài đoạn thẳng ngắn nhất trong số các đoạn thẳng mà một đầu mút thuộc hình vuông A và đầu mút kia thuộc hình vuông B. 11
  • 12. Yêu cầu: Tìm hai hình vuông xa nhau nhất trong số N hình vuông cho trước. Dữ liệu: Vào từ file văn bản SQUARE.INP: • Dòng đầu tiên chứa số N. • Dòng thứ i trong N dòng tiếp theo chứa 4 số xi, yi, zi và ti. Kết quả: Ghi ra file văn bản SQUARE.OUT trong đó chứa chỉ số của hai hình vuông xa nhau nhất mà bạn tìm được. Ví dụ: SQUARE.INP SQUARE.OUT 3 1 1 3 3 2 2 5 5 7 1 8 2 1 3 Lời giải Vấn đề quan trọng nhất trong bài toán này là xây dựng thủ tục tính khoảng cách giữa hai hình vuông một cách hiệu quả. Để viết thủ tục này, ta sẽ phân chia công việc cần thực hiện thành nhiều công việc con. Nhưng trước tiên cần nhận xét rằng, luôn tồn tại một đoạn thẳng ngắn nhất nối giữa hai hình vuông mà có ít nhất một đầu mút là một trong các đỉnh của hai hình vuông. Thật vậy, với một đoạn thẳng mà hai đầu mút đều không phải là đỉnh của hình vuông, ta có thể dời hai đầu mút sao cho đoạn thẳng mới không dài hơn đoạn thẳng cũ và ít nhất một đầu mút sẽ trở thành đỉnh của hình vuông. Vậy thủ tục tính khoảng cách giữa hai hình vuông sẽ được xây dựng từ trên xuống như sau: • Khoảng cách giữa hai hình vuông bằng khoảng cách ngắn nhất trong số các khoảng cách từ một trong 8 đỉnh của hai hình vuông đến hình vuông kia • Khoảng cách từ một điểm đến một hình vuông bằng khoảng cách ngắn nhất trong số các khoảng cách từ điểm đó đến bốn cạnh của hình vuông Khi đã có thủ tục tính khoảng cách giữa hai hình vuông, ta chỉ cần duyệt qua tất cả các cặp hình vuông và chọn ra cặp có khoảng cách xa nhất. Do giới hạn của bài toán là số hình vuông N ≤ 2000 nên cách làm này là khả thi, thời gian chạy là O(N2 ). Sau này các bạn sẽ được tiếp cận các lời giải hiệu quả hơn. Chương trình: const MAXN=2020; finp='square.inp'; fout='square.out'; var x1, y1, x2, y2: array[1..MAXN] of longint; n, luui, luuj: longint; 12
  • 13. function min(a,b: longint): longint; begin if a<b then min:=a else min:=b; end; function kc(x,a,b: longint): longint; begin if (a<=x) and (x<=b) then kc:=0 else if (x<a) then kc:=a-x else kc:=x-b; end; function kcdoan(x,y,y0,x1,x2: longint): longint; begin kcdoan:=sqr(y0-y)+sqr(kc(x,x1,x2)); end; function kchinhvuong(x,y,x1,y1,x2,y2:longint):longint; var kq: longint; begin kq:=kcdoan(x,y,y1,x1,x2); kq:=min(kq,kcdoan(x,y,y2,x1,x2)); kq:=min(kq,kcdoan(y,x,x1,y1,y2)); kq:=min(kq,kcdoan(y,x,x2,y1,y2)); kchinhvuong:=kq; end; function kc2hinh(i,j: longint): longint; var kq: longint; begin kq:=kchinhvuong(x1[i],y1[i],x1[j],y1[j],x2[j],y2[j]); kq:=min(kq,kchinhvuong(x1[i],y2[i],x1[j],y1[j],x2[j],y2[j])); kq:=min(kq,kchinhvuong(x2[i],y1[i],x1[j],y1[j],x2[j],y2[j])); kq:=min(kq,kchinhvuong(x2[i],y2[i],x1[j],y1[j],x2[j],y2[j])); if (i<j) then kq:=min(kq,kc2hinh(j,i)); kc2hinh:=kq; end; procedure nhap; var i: longint; begin readln(n); for i:=1 to n do readln(x1[i], y1[i], x2[i], y2[i]); end; procedure xuly; var i, j, d, xanhat: longint; begin xanhat:=-1; 13
  • 14. for i:=1 to n-1 do for j:=i+1 to n do begin d:=kc2hinh(i,j); writeln(d); if d>xanhat then begin xanhat:=d; luui:=i; luuj:=j; end; end; end; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); nhap; xuly; writeln(luui,' ',luuj); close(input); close(output); end. Năm học 2004-2005 BÀI 1: KHOẢNG CÁCH GIỮA HAI SỐ Tên chương trình: DISTANCE.PAS Với hai chữ số x và t, khoảng cách của chúng được định nghĩa là số nguyên không âm nhỏ nhất d(x,y) mà khi cộng thêm d(x,y) vào một chữ số nào đó trong hai chữ số x,y thì kết quả nhận được là một số nguyên có chữ số hàng đơn vị trùng với chữ số còn lại. Ví dụ: d(2,5)=3 vì 2+3=5, d(5,1)=4 vì 1+4=5, còn d(1,9)=2 vì 9+2 = 11. Với hai số nguyên dương X và Y có cùng số lượng chữ số, khoảng cách d(X,Y) giữa hai số X và Y là tổng khoảng cách giữa các cặp chữ số cùng hàng tương ứng. Ví dụ d(213,419)=d(2,4) + d(1,1) + d(3,9) = 2 + 0 + 4 = 6. Bài toán: Cho hai số X và Y có cùng lượng chữ số N (0 < N < 100), hãy tìm khoảng cách d(X,Y). Dữ liệu vào từ file văn bản DISTANCE.INP trong đó dòng đầu chứa số X; dòng thứ hai chứa số Y thỏa mãn dàng buộc của bài toán. Kết quả ghi ra file văn bản DISTANCE.OUT trong đóchứa một số nguyên duy nhất là kết quả d(X,Y) tìm được. Ví du: DISTANCE.INP DISTANCE.OUT 213 419 6 14
  • 15. Lời giải: Ta xây dựng hàm d(a,b) tính khoảng cách hai chữ số a và b theo định nghĩa của bài toán, sau đó dùng hàm này để tính tổng khoảng cách giữa các cặp chữ số của hai số. Hàm d(a,b) được thể hiện qua đoạn lệnh sau: function d(a,b:byte):byte; var i: byte; begin for i:=0 to 9 do {duyệt qua các chữ số từ 0 đến 9} if ((a+i) mod 10=b) or {nếu cộng thêm i vào a mà thu được b} ((b+i) mod 10=a) then {hoặc ngược lại...) begin d:=i; {trả về chữ số i} exit; end; end; Chương trình: const finp='distance.inp'; fout='distance.out'; function d(a,b:byte):byte; var i: byte; begin for i:=0 to 9 do if ((a+i) mod 10=b) or ((b+i) mod 10=a) then begin d:=i; exit; end; end; var x, y: string; i,s: longint; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); readln(x,y); s:=0; for i:=1 to length(x) do s:=s+d(ord(x[i])-ord('0'),ord(y[i])-ord('0')); writeln(s); close(input); 15
  • 16. close(output); end. BÀI 2: TẠO BẢNG Tên chương trình: TABLE.PAS Cho một bảng A gồm N x N số nguyên (N ≤ 100), các dòng được đánh số từ trên xuống dưới bắt đầu từ 1, các cột được đánh số từ trái qua phải cũng bắt đầu từ 1. Mỗi số trong bảng có giá trị tuyệt đối không vượt quá 30000. Bảng B được tạo ra từ bảng A theo qui tắc sau: Phần tử của B nằm ở dòng I, cột j có giá trị bằng tổng của các số nằm trong ô (i,j) và các ô kề nó trong bảng A: Bij = Aij+A(i+1)j+A(i-1)j+Ai(j+1)+Ai(j-1) Chú ý: Các phần tử nằm ngoài bảng được xem như có giá trị bằng 0. Bài toán: Cho bảng A. Hãy tạo ra bảng B tương ứng. Dữ liệu vào cho trong file văn bản TABLE.INP trong đó : • Dòng đầu chứa số N. • Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các phần tử nằm trên dòng thứ i của bảng A. • Các số trên cùng một dòng cách nhau bởi khỏang trắng. Kết quả ghi ra file văn bản TABLE.OUT cho biết bảng B tạo được có định dạng cùng một qui cách với file input, nghĩa là: Dòng đầu chứ số N. Dòng thứ i trong N dòng tiếp theo chứa N số nguyên lần lượt ứng với các phần tử nằm trên dòng thứ i của bảng B. Các số trên cùng một dòng cách nhau bởi khỏang trắng. Ví dụ: TABLE.INP TABLE.OUT 4 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 4 8 12 16 15 21 28 31 25 27 34 31 23 18 20 16 11 Lời giải: Ta in ra các phần tử của mảng B theo công thức như đề bài nêu: for i:=1 to n do begin for j:=1 to n do write(a[i,j]+a[i,j-1]+a[i-1,j]+a[i,j+1]+a[i+1,j],' '); writeln; end; Khi cài đặt chú ý để khai báo dư ra các phần tử biên trên mảng A và đặt chúng bằng giá trị 0: 16
  • 17. var a: array[0..MAXN+1, 0..MAXN+1] of longint;{0,n+1 là chỉ số của các hàng, cột biên} ... fillchar(a,sizeof(a),0); {tất cả các phần tử, bao gồm phần tử biên sẽ bằng 0} Chương trình: const finp='table.inp'; fout='table.out'; MAXN=105; var a: array[0..MAXN+1, 0..MAXN+1] of longint; n,i,j: longint; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); fillchar(a,sizeof(a),0); readln(n); for i:=1 to n do for j:=1 to n do read(a[i,j]); writeln(n); for i:=1 to n do begin for j:=1 to n do write(a[i,j]+a[i,j-1]+a[i-1,j]+a[i,j+1]+a[i+1,j],' '); writeln; end; close(input); close(output); end. BÀI 3: TÌM NGHIỆM Tên chương trình: EQUA.PAS Xét một phương trình có dạng sau: x + y+ z =K trong đó K là một số nguyên dương. Phương trình này có thể có vô số nghiệm. Tuy nhiên, ở đây người ta chỉ quan tâm đến các nghiệm (x,y,z) mà trong đó các số x,y,z đều là các số nguyên tố. Nhắc lại: số tự nhiên p được gọi là số nguyên tố nếu p>1 và p chỉ chia hết cho 1 và chính nó. Bài toán: Với số K cho trước (K < 5000), hãy tìm tất cả các bộ số nguyên tố x,y,z (x ≤ y ≤ z) là nghiệm của phương trình trên hoặc cho biết không có nghiệm thoả mãn yêu cầu bài toán. 17
  • 18. Dữ liệu vào cho trong file văn bản EQUA.INP trong đó chứa duy nhất số K Kết quả ghi ra file văn bản EQUA.OUT chứa N + 1 dòng (N là số nghiệm tìm được), trong đó: • Dòng thứ i trong N dòng đầu tiên chứa 3 số nguyên cho biết bộ nghiệm thứ i tìm được. • Dòng thứ N + 1 chứa 3 số 0 cho biết điểm kết thúc file output. • Các số trên cùng một dòng cách nhau bởi khoảng trắng. Ví dụ: EQUA.INP EQUA.OUT 4 0 0 0 EQUA.INP EQUA.OUT 7 2 2 3 0 0 0 Lời giải Ta kiểm tra các số nguyên tố từ 1 đến 5000 (giá trị lớn nhất của K) rồi lưu vào một mảng: for i:=2 to MAXK do if (nguyento(i)) then begin {nếu i là số nguyên tố} inc(n); p[n]:=i; {lưu i vào mảng p} nt[i]:=true; {đặt lại cờ đánh dấu cho biết i là số nguyên tố} end; Sau đó dùng hai vòng lặp qua mảng này để duyệt qua x và y. Mảng đánh dấu nt dùng để kiểm tra nhanh xem z=K-x-y có phải là số nguyên tố không: for i:=1 to n do {i là chỉ số của số nguyên tố x trong mảng p} for j:=i to n do {j là chỉ số của số nguyên tố y trong mảng p} begin z:=k-p[i]-p[j]; {z=k-x-y} if (nt[z]) and (p[j]<z) then {kiểm tra z có phải số nguyên tố không?} writeln(p[i], ' ',p[j],' ', z); {in ra x, y, z} end; Thời gian chạy của chương trình là O(N2 ) với N là số lượng số nguyên tố. N vào khoảng 700 nên chương trình hoàn toàn chạy trong thời gian cho phép. Chương trình: const finp='equa.inp'; fout='equa.out'; MAXK=5010; function nguyento(n: longint): boolean; var i: longint; begin nguyento:=false; 18
  • 19. for i:=2 to trunc(sqrt(n)) do if n mod i = 0 then exit; nguyento:=true; end; var n, k, i, j, z: longint; nt: array[2..MAXK] of boolean; p: array[1..MAXK] of longint; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); fillchar(nt,sizeof(nt),0); for i:=2 to MAXK do if (nguyento(i)) then begin inc(n); p[n]:=i; nt[i]:=true; end; readln(k); for i:=1 to n do for j:=i to n do begin z:=k-p[i]-p[j]; if (nt[z]) and (p[j]<z) then writeln(p[i], ' ',p[j],' ', z); end; writeln('0 0 0'); close(input); close(output); end. Năm học 2005-2006 Bài 1: Trộn mảng Cho hai mảng số nguyên dương A và B lần lượt có N và M số (0 < N, M <50 000). Các phần tử trong cả hai mảng A và B đều được sắp theo thứ tự tăng dần. Yêu cầu: hãy tạo mảng C gồm N+M phần tử từ tất cả các phần tử của A và B sao cho các phần tử của C cũng có thứ tự tăng dần. Dữ liệu cho trong 2 file văn bản có tên A.INP và B.INP. Dòng đầu của file A.INP chứa số N. Mỗi dòng trong N dòng tiếp theo chứa 1 số nguyên dương ứng với các phần tử của mảng A. 19
  • 20. Dòng đầu của file B.INP chứa số M. Mỗi dòng trong M dòng tiếp theo chứa 1 số nguyên dương ứng với các phần tử của mảng B. Kết quả xuất ra file văn bản C.OUT gồm N+M dòng, lần lượt chứa các phần tử của mảng C. Ví dụ: A . INP 3 1 2 5 B . INP 2 2 4 C . OUT 1 2 2 4 5 Lời giải Trong bài toán này ta hoàn toàn không cần lưu trữ mảng A và B vào bộ nhớ. Thuật toán của ta sẽ đọc lần lượt các số từ hai mảng A, B, xử lý và in ra trực tiếp mảng C. Ta quản lý hai biến chạy i và j tương ứng trên mảng A và B. Ban đầu i và j được gán giá trị 1, nghĩa là vị trí đầu tiên của mảng. Tại một thời điểm, biến chạy i, j cho biết ta đã đọc đến phần tử Ai và Bj. Quy tắc tăng giá trị biến chạy là như sau: • Nếu Ai ≤ Bj: in ra giá trị Ai, tăng biến chạy i, đọc giá trị mới từ mảng A. Đoạn mã sau đây thể hiện điều này: {biến a, b chỉ phần tử đang xét Ai, Bj trên hai mảng} if (a<=b) then {điều kiện Ai ≤ Bj} begin writeln(fc,a); {in ra Ai như là phần tử tiếp theo của mảng C} inc(i); {tăng biến i} if (i<=n) then {nếu vẫn chưa đọc hết mảng A} readln(fa,a) {đọc số mới} else 20
  • 21. a:=maxlongint; {nếu đã đọc hết mảng A, gán a giá trị bằng ∞ để sau này khi so sánh, chỉ các phần tử của mảng B mới được xét} end else begin ... {tương tự cho trường hợp Ai > Bj} end; • Nếu Ai > Bj: tương tự ta in ra giá trị Bj, tăng biến chạy j và đọc giá trị mới từ mảng B Thuật toán kết thúc khi ta đã đọc hết các số, nghĩa là khi hai biến chạy đều vượt ra khỏi phạm vi của mảng: i > N và j > M. Ta có thể dùng vòng lặp while sau đây để kiểm tra điều kiện này: while (i<=n) or (j<=m) do begin ... end; Thời gian chạy của thuật toán là tuyến tính theo số phần tử của hai mảng: O(M+N). Chương trình: var fa,fb,fc: text; n, m, i, j, a, b: longint; begin assign(fa,'a.inp'); reset(fa); assign(fb,'b.inp'); reset(fb); assign(fc,'c.out'); rewrite(fc); readln(fa,n); readln(fb,m); i:=1; readln(fa,a); j:=1; readln(fb,b); while (i<=n) or (j<=m) do begin if (a<=b) then begin writeln(fc,a); inc(i); if (i<=n) then readln(fa,a) else a:=maxlongint; end else begin writeln(fc,b); inc(j); if (j<=m) then readln(fb,b) else 21
  • 22. b:=maxlongint; end; end; close(fa); close(fb); close(fc); end. Bài 2:Hình chữ nhật Cho N hình chữ nhật (2 <N< 500) có các cạnh song song với hai trục tọa độ và tọa độ các đỉnh đều nguyên. Các hình chữ nhật được đánh số từ 1 đến N. Yêu cầu: Hãy tìm hai hình chữ nhật mà phần giao nhau của chúng có diện tích lớn nhất. Dữ lieu cho trong file văn bản có tên là HCN.INP: Dòng đầu chứa số N. Dòng thứ I trong N dòng tiếp theo mô tả hình chữ nhật thứ I, chứa 4 số nguyên x1, y1, x2, y2 ứng với các hòanh độ và tung độ của các hình chữ nhật (- 10000< x1, < x2 <10000; -10000< y1, < y2 <10000). Kết quả xuất ra file văn bản HCN.OUT gồm 1 dòng duy nhất, chứa 2 số nguyên dương cho biết chỉ số của 2 hình chữ nhật tìm được. Ví dụ: HCN . INP 3 1 1 5 5 -5 -5 5 5 10 10 1000 1000 Lời giải: Chúng ta cần viết thủ tục tính diện tích phần giao giữa hai hình chữ nhật. Chú ý phần giao của hai hình chữ nhật cũng là một hình chữ nhật và có chiều dài/rộng bằng phần giao của hai chiều dài/rộng tương ứng. Do đó ta chỉ cần viết thủ tục tìm phần giao giữa hai đoạn thẳng trên trục số và sử dụng. Thủ tục sau tìm giao giữa hai đoạn thẳng (a1,b1) và (a2,b2) trên trục số: function giaodoan(a1,b1,a2,b2:longint):longint; begin if (b1<=a2) or (a1>=b2) then giaodoan:=0 {hai đoạn không giao nhau} else giaodoan:=min(b1,b2)-max(a1,a2); {hai đoạn giao nhau} end; 22 HCN . OUT 1 2
  • 23. Thủ tục giaohcn sau áp dụng thủ tục giaodoan để tìm diện tích phần giao của hai hình chữ nhật: function giaohcn(i,j: longint): longint; begin {phần giao là một hình chữ nhật có chiều dài/rộng là phần giao của hai chiều dài/rộng ban đầu} giaohcn:=giaodoan(x1[i],x2[i],x1[j],x2[j])*giaodoan(y1[i],y2[i],y1[j],y2[j]); end; Sau khi có thủ tục tính diện tích phần giao giữa hai hình chữ nhật, ta xét qua tất cả các cặp hình chữ nhật và chọn ra cặp có phần giao lớn nhất: for i:=1 to n-1 do for j:=i+1 to n do begin s:=giaohcn(i,j); if s>kq then {tìm được hai hình có diện tích phần giao lớn hơn} begin kq:=s; {cập nhật kết quả} luui:=i; {lưu lại chỉ số của hai hình} luuj:=j; end; end; Chương trình: const MAXN=505; finp='hcn.inp'; fout='hcn.out'; var x1,y1,x2,y2: array[1..MAXN] of longint; function min(a,b:longint):longint;begin if a<b then min:=a else min:=b; end; function max(a,b:longint):longint;begin if a>b then max:=a else max:=b; end; function giaodoan(a1,b1,a2,b2:longint):longint; begin if (b1<=a2) or (a1>=b2) then giaodoan:=0 else giaodoan:=min(b1,b2)-max(a1,a2); end; function giaohcn(i,j: longint): longint; begin giaohcn:=giaodoan(x1[i],x2[i],x1[j],x2[j])*giaodoan(y1[i],y2[i],y1[j],y2[j]); end; var n, kq, s, i, j, luui, luuj: longint; begin assign(input,finp); reset(input); assign(output,fout); 23
  • 24. rewrite(output); kq:=0; readln(n); for i:=1 to n do readln(x1[i],y1[i],x2[i],y2[i]); for i:=1 to n-1 do for j:=i+1 to n do begin s:=giaohcn(i,j); if s>kq then begin kq:=s; luui:=i; luuj:=j; end; end; writeln(kq); writeln(luui,' ',luuj); close(input); close(output); end. Bài 3: So sánh Cho 2 số nguyên dương A,B (0<A, B< 10100 ). Yêu cầu: hãy so sánh giá trị của 2 số. Dữ liệu cho trong file văn bản có tện SO.INP gồm 2 dòng: Dòng đầu chứa số A. Dòng thứ 2 chứa số B. Kết quả xuất ra file văn bản SO.OUT gồm 1 dòng duy nhất, chứa số -1,0 hoặc 1 lần lượt tương ứng với các trường hợp sau: A < B, A = B, và A > B. Ví dụ: SO.INP SO.OUT 12345678900000001 12345678900000000 1 Lời giải: Thủ tục so sánh hai số A, B có thể được xây dựng như sau: • Xem A, B là xâu ký tự. Nếu độ dài của hai xâu khác nhau, ta thêm chữ số 0 vào đầu xâu ngắn hơn cho đến khi độ dài của hai xâu bằng nhau : while (length(a)<length(b)) do a:='0'+a; while (length(b)<length(a)) do b:='0'+b; 24
  • 25. • Sau đó ta duyệt các chữ số từ trái sang phải, nếu chữ số tương ứng của A bé hơn/lớn hơn của B thì kết luận A<B hay A>B và thoát khỏi thủ tục. Nếu tất cả các cặp chữ số đều giống nhau thì ta kết luận A=B. for i:=1 to length(a) do if (a[i]<b[i]) then begin sosanh:=-1; {kết luận A < B} exit; {thoát khỏi thủ tục} end else if (a[i]>b[i]) then begin sosanh:=1; {kết luận A > B} exit; {thoát khỏi thủ tục} end; sosanh:=0; {kết luận A = B} Chương trình: const finp='so.inp'; fout='so.out'; function sosanh(a,b:string):longint; var i: longint; begin while (length(a)<length(b)) do a:='0'+a; while (length(b)<length(a)) do b:='0'+b; for i:=1 to length(a) do if (a[i]<b[i]) then begin sosanh:=-1; exit; end else if (a[i]>b[i]) then begin sosanh:=1; exit; end; sosanh:=0; end; var a, b: string; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(a); readln(b); writeln(sosanh(a,b)); close(input); close(output); 25
  • 26. end. Bài 4: Bảng vuông Cho một bảng vuông các số nguyên kích thước NxN (2 < N< 100) mà mỗi phần tử là một số nguyên không âm và giá trị không vượt quá 100. Yêu cầu: hãy tìm một bảng vuônmg con của bảng đã cho mà các phần tử của nó chứa toàn số dương và tổng các phần tử thuộc bảng con này có giá trị lớn nhất. Dữ liệu chop trong file văn bản có tên BANG.INP: Dòng đầu chứa số N. Dòng thứ I trong N dòng tiếp theo chứa N số nguyên dương ứng với dòng thứ i của bảng. Kết quả xuất ra file văn bản BANG.OUT chứa 1 số nguyên duy nhất chứa giá trọ tổng lớn nhất tìm được. Ví dụ: BANG . INP BANG . OUT 3 1 1 0 1 2 1 1 1 2 6 Lời giải Để giải quyết bài toán này, ta dùng một phương pháp thường gặp khi xử lý các phép tính tổng trên bảng số: dùng bảng lưu tổng bộ phận. Cụ thể là, ta sẽ dùng bảng s với ý nghĩa: s[i,j] là tổng các số trên bảng từ ô (1,1) đến ô (i,j). Mỗi khi đọc phần tử (i, j), bảng s sẽ được cập nhật theo công thức s[i,j]:=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+a[i,j]; Và để tính tổng các số trong hình chữ nhật (i1,j1) đến (i2,j2) ta dùng công thức: s[i2,j2]-s[i1-1,j2]-s[i2,j1-1]+s[i1-1,j1-1] Để xử lý điều kiện bảng số chứa toàn số dương, ta dùng thêm bảng c theo kỹ thuật tương tự bảng s với ý nghĩa: c[i,j] là số số 0 trên bảng ban đầu trong hình chữ nhật từ ô (1,1) đến ô (i,j). Để xét xem một vùng hình chữ nhật có gồm toàn số dương hay không, ta dùng công thức tính tổng như trên và xem kết quả có bằng 0 hay không. Để duyệt qua các hình vuông con trên bảng số, ta xét tất cả các đỉnh trên trái (i,j) và độ dài cạnh hình vuông L. Tổng các số trong hình vuông và điều kiện chứa toàn số dương được tính theo công thức như trình bày ở trên. Thời gian thực hiện của thuật toán là O(N3 ), hoàn toàn khả thi với giới hạn N ≤ 100. Chương trình mẫu 26
  • 27. const MAXN=105; finp='bang.inp'; fout='bang.out'; type bang=array[0..MAXN, 0..MAXN] of longint; function max(a,b:longint): longint; begin if a>b then max:=a else max:=b; end; function sum(a: bang; i1,j1,i2,j2: longint): longint; begin sum:=a[i2,j2]-a[i1-1,j2]-a[i2,j1-1]+a[i1-1,j1-1]; end; var c, s: bang; n,i,j,l,x,i1,j1,i2,j2,kq: longint; begin assign(input,'bang.inp'); reset(input); assign(output,'bang.out'); rewrite(output); fillchar(s,sizeof(s),0); fillchar(c,sizeof(c),0); readln(n); for i:=1 to n do for j:=1 to n do begin read(x); s[i,j]:=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+x; c[i,j]:=c[i-1,j]+c[i,j-1]-c[i-1,j-1]; if (x=0) then inc(c[i,j]); end; kq:=0; for i1:=1 to n do for j1:=1 to n do begin l:=0; repeat i2:=i1+l; j2:=j1+l; if sum(c,i1,j1,i2,j2)=0 then kq:=max(kq,sum(s,i1,j1,i2,j2)) else break; inc(l); 27
  • 28. until (i2>n) or (j2>n); end; writeln(kq); close(input); close(output); end. Năm học 2006-2007 ĐOẠN CON DÀI NHẤT (Bài 1 – Tuyển sinh 2006 - 2007) Cho chuỗi kí tự S gồm toàn các chữ cái in hoa (A…Z) với độ dài không vượt quá 255. Hãy tìm đoạn con các kí tự liên tiếp dài nhất sao cho không có kí tự nào xuất hiện nhiều hơn một lần. Trong trường hợp có nhiều hơn một đoạn con có cùng chiều dài dài nhất, hãy chỉ ra đoạn xuất hiện đầu tiên trong chuỗi S. Dữ liệu: Vào từ văn bản SUBSTR.INP gồm một dòng duy nhất chứa chuỗi S. Kết quả: Ghi ra file văn bản SUBSTR.OUT hai số nguyên P và L tương ứng là vị trí và chiều dài của đoạn con dài nhất tìm được (kí tự đầu tiên trong chuỗi có vị trí là 1). Ví dụ: SUBSTR.INP SUBSTR.OUT ABABCDAC 3 4 Lời giải Ta duyệt qua lần lượt các ký tự của chuỗi. Khi duyệt qua ký tự thứ i, ta sẽ tìm cách xây dựng chuỗi dài nhất không có ký tự nào xuất hiện hai lần và kết thúc tại vị trí i. Để xây dựng được chuỗi này, ta cần lưu trữ các thông tin sau đây: • Vị trí bắt đầu của chuỗi dài nhất mà không có ký tự nào xuất hiện hai lần, ta ký hiệu là p. • Mảng b['A'..'Z'] trong đó với ký tự c thì b[c] lưu vị trí xuất hiện cuối cùng của ký tự c. Khi duyệt qua ký tự thứ i (ký hiệu là s[i]): • Nếu vị trí xuất hiện cuối cùng của ký tự này không nhỏ hơn p, nghĩa là ký tự này sẽ xuất hiện 2 lần và làm cho chuỗi đang xét trở nên không hợp lệ. Khi đó ta cập nhật lại vị trí bắt đầu p bằng b[s[i]]+1. • Ta cập nhật lại giá trị i cho b[s[i]] để đảm bảo ý nghĩa của mảng b • Chuỗi đang xét sẽ bắt đầu từ vị trí p và kết thúc tại vị trí i, do đó chuỗi này có độ dài là i-p+1, ta dùng giá trị này so sánh với kết quả để tìm ra chuỗi dài nhất. Đoạn chương trình sau đây thể hiện thuật toán: p:=1; {gán giá trị khởi tạo cho p: vị trí bắt đầu là 1} for i:=1 to length(s) do {duyệt qua từng ký tự của chuỗi} begin 28
  • 29. if (b[s[i]]>=p) then {ký tự s[i] xuất hiện hai lần trong chuỗi đang xét!} p:=b[s[i]]+1; {cập nhật lại vị trí bắt đầu mới} b[s[i]]:=i; {cập nhật lại vị trí xuất hiện của ký tự s[i]} if (i-p+1>l) then begin {i-p+1 là độ dài của chuỗi đang xét} {so sánh với độ dài lớn nhất l} luup:=p; {lưu lại vị trí bắt đầu} l:=i-p+1; {cập nhật lại độ dài lớn nhất} end; end; Chương trình: const finp='substr.inp'; fout='substr.out'; var b:array['A'..'Z'] of longint; s:string; p,i,l,luup: longint; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(s); fillchar(b,sizeof(b),0); p:=1; for i:=1 to length(s) do begin if (b[s[i]]>=p) then p:=b[s[i]]+1; b[s[i]]:=i; if (i-p+1>l) then begin luup:=p; l:=i-p+1; end; end; writeln(luup,' ',l); close(input); close(output); end. ĐƯỜNG ĐI (Bài 2 – Tuyển sinh 2006 - 2007) Một con robot di chuyển theo một chương trình định sẵn trên mặt phẳng toạ độ. Chương trình này được thể hiện dưới dạng một dãy N lệnh (1 ≤N ≤3000). Các lệnh thuộc một trong các dạng sau: • F S: Đi thẳng theo hướng hiện tại S bước. • R S: Rẽ phải 900 và đi S bước. • L S: Rẽ trái 900 và đi S bước. 29
  • 30. Yêu cầu: Cho một chương trình điều khiển robot, hãy xác định chiều dài T đoạn đường mà con robot đã đi được, biết mỗi bước của nó dài d(cm). Ban đầu con robot đứng tại vị trí (0,0) và hướng theo chiều dương của trục hoành. Dữ liệu: Vào từ file văn bản PATH.INP: • Dòng đầu tiên chứa 2 số nguyên dương N và d. • N dòng tiếp theo, mỗi dòng chứa một lệnh theo quy cách nêu trên. Kết quả: Ghi ra file PATH.OUT chứa chiều dài T tìm được. Ví dụ: PATH.INP PATH.OUT 4 1 F 5 R 7 F 2 L 9 23 Lời giải: Kết quả của bài toán này bằng tích của d và tổng của độ dài các bước đi. Các lệnh F, R, L không ảnh hưởng đến kết quả của bài toán và chỉ được đưa ra để kiểm tra thí sinh có thuần thục trong việc đọc dữ liệu hay không. Mỗi câu lệnh sẽ gồm hai ký tự (một chữ cái chỉ lệnh và một khoảng trắng) và một số, do đó ta đọc vào hai biến kiểu ký tự và một biến kiểu số nguyên: readln(c,c,x); Ta sẽ sử dụng biến x, còn c chỉ là biến tạm không được dùng đến. Chương trình mẫu const finp='path.inp'; fout='path.out'; var i, n, d, x, s: longint; c: char; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(n,d); for i:=1 to n do begin readln(c,c,x); s:=s+x; end; writeln(x); close(input); close(output); end. 30
  • 31. ĐẾM VÙNG (Bài 3 – Tuyển sinh 2006 - 2007) Một khu vườn hình chữ nhật được chia thành NM × ô đơn vị. Các dòng đánh số từ 1 tới M từ trên xuống dưới, các cột đánh số từ 1 đến N từ trái sang phải. Ô nằm ở hàng i, cột j được gọi là ô (i,j). Người ta có đắp K lối đi trên mảnh vườn đó, lối đi thứ i là một dãy các ô liên tiếp nhau theo đường ngang hoặc đường dọc, và được cho bởi 4 số nguyên dương xi, yi, zi và ti trong đó (xi, yi) là vị trí của ô đầu, còn (zi, ti) là vị trí của ô cuối của lối đi. Các lối đi chia khu vườn thành các miền. Mỗi miền là một tập tất cả các ô không thuộc các lối đi sao cho hai ô bất kì trong đó có thể đi tới bằng cách di chuyển qua các ô chung cạnh và không phải là ô thuộc lối đi. Yêu cầu: Hãy xác định số miền S mà các lối đi chia khu vườn. Dữ liệu: Vào từ file văn bản REGIONS.INP trong đó: • Dòng đầu chứa 3 số M, N, K. • Dòng thứ i trong K dòng tiếp theo chứa 4 số xác định lối đi thứ i: xi, yi, zi, ti. Kết quả: Ghi ra file văn bản REGIONS.OUT số S tìm được. Ví dụ: REGIONS.INP REGIONS.OUT 10 10 2 5 1 5 10 1 5 7 5 3 SỐ ƯỚC (Bài 4 – Tuyển sinh 2006 - 2007) Cho số nguyên dương N. Giai thừa của N, kí hiệu là N!, là tích của các số tự nhiên từ 1 đến N. Gọi T là số lượng ước lớn hơn 1 của N!. Ví dụ với N = 4, ta có 4! = 24. Như vậy 4! có 7 ước lớn hơn 1 là: 2, 3, 4, 6, 8, 12, 24. Yêu cầu: Cho N, hãy xác định T. Dữ liệu: Vào từ file văn bản DIVISORS.INP trong đó chứa duy nhất số N (N ≤20, trong đó 50% số test có N ≤10). Kết quả: Ghi ra file văn bản DIVISORS.OUT số T tìm được. Ví dụ: DIVISORS.INP DIVISORS.OUT 4 7 Lời Giải Nếu một số nguyên m được phân tích ra thừa số nguyên tố dưới dạng m=p1 a1 p2 a2 ...pk ak thì số ước số của m bằng tích (a1+1)(a2+1)...(ak+1). Ta sẽ tìm cách phân tích n! ra thừa số nguyên tố. Các ước số nguyên tố của n! chỉ nằm trong phạm vi từ 2 đến n, do đó ta duyệt qua tất cả các số nguyên tố trong phạm vi này. Với số nguyên tố p, số mũ của nó trong biểu diễn nguyên tố của n! bằng: 31
  • 32. [n/p] + [n/p2 ] + [n/p3 ] + ... Trong đó [n/p] ký hiệu phần nguyên của n/p, nói cách khác trong ngôn ngữ Pascal [n/p] bằng n div p. Công thức trên được giải thích như sau: [n/p] là số lượng số từ 1 đến n chia hết cho p, [n/p2 ] là số lượng số từ 1 đến n chia hết cho p2 ,... mỗi số này đều đóng góp một thừa số nguyên tố p phân biệt cho n!. Đoạn chương trình sau đây thể hiện thuật toán: for i:=2 to n do if nguyento(i) then {duyệt qua các số nguyên tố từ 2 đến n} begin a:=0; j:=i; {biến j sẽ duyệt qua các số mũ của i là i, i2 , i3 ,...} while (j<=n) do {đến khi j>n thì dừng lại vì khi đó [n/j] sẽ luôn bằng 0} begin a:=a+n div j; j:=j*i; end; {đến đây a=[n/i]+[n/i2 ]+... chính là số thừa số i trong biểu diễn nguyên tố của n!} d:=d*(a+1); {nhân thêm (a+1) vào số ước, theo công thức tính số ước nêu trên} end; Chương trình: function nguyento(n: longint): boolean; var i: longint; begin nguyento:=false; for i:=2 to trunc(sqrt(n)) do if n mod i = 0 then exit; nguyento:=true; end; var n,i,j,a,d: longint; begin readln(n); d:=1; for i:=2 to n do if nguyento(i) then begin a:=0; j:=i; while (j<=n) do begin a:=a+n div j; j:=j*i; end; d:=d*(a+1); end; writeln(d-1); end. 32
  • 33. Năm học 2007-2008 TÍCH LỚN NHẤT (Bài 1 – Tuyển sinh 2007 - 2008) 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á 30000. Kết quả: Ghi ra file văn bản TICHMAX.OUT một số duy nhất T. Ví dụ: TICHMAX.INP TICHMAX.OUT 9 3 5 1 7 9 0 9 -3 10 810 Lời giải 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ự. Chú ý vì kết quả có thể vượt quá kiểu số nguyên 32 bit nên ta cần khai báo với kiểu số nguyên 64 bit (trình biên dịch Free Pascal hỗ trợ kiểu số nguyên 64 bit với tên gọi int64): var 33
  • 34. ... kq:int64; ... kq:=max(max1*max2*max3,min1*min2*max1); Chương trình: const finp='tichmax.inp'; fout='tichmax.out'; var max1,max2,max3,min1,min2,x,i,n:longint; kq:int64; function max(a,b:longint):longint; begin if a>b then max:=a else max:=b; end; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); max1:=-maxlongint; max2:=max1; max3:=max1; readln(n); for i:=1 to n do begin read(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 begin max3:=x; end; if (x<=min1) then begin min2:=min1; min1:=x; end else if (x<=min2) then min2:=x; end; 34
  • 35. kq:=max(max1*max2*max3,min1*min2*max1); writeln(kq); close(input); close(output); end. TÌM DÃY K (Bài 2 – Tuyển sinh 2007 - 2008) Cho một xâu S có độ dài N. Với mỗi số nguyên i (1 ≤i ≤N), xét xâu con Si gồm i kí tự đầu của xâu S. Người ta cần xác định giá trị Ki cho mỗi xâu Si là giá trị lớn nhất thỏa mãn điều kiện sau: Nếu xâu X là xâu con gồm Ki kí tự đầu của xâu Si và tạo xâu Y bằng cách viết Ki kí tự cuối cùng của xâu Si theo thứ tự ngược từ cuối lên đầu ta có X = Y. Ví dụ, nếu S = ‘acbaca’ thì S4 = ‘acba’ và vì vậy K4 = 1, vì khi đó X = Y = ‘a’. S6 = ‘acbaca’ và vì vậy K6 = 2 do khi đó X = Y = ‘ac’. Yêu cầu: Cho xâu S độ dài N, tìm các số K1, K2, …, KN. Dữ liệu: Vào từ file văn bản DAYK.INP: • Dòng đầu tiên chứa số nguyên dương N (N ≤255). • Dòng thứ hai chứa xâu S độ dài N. Kết quả: Ghi ra file văn bản DAYK.OUT trên một dòng duy nhất dãy số K1, K2, …, KN. Ví dụ: DAYK.INP DAYK.OUT 6 acbaca 1 0 0 1 0 2 Lời giải Ta dùng vòng lặp i từ 1 đến N, với mỗi giá trị i ta tìm cách xác định số Ki theo như yêu cầu của đề bài. Số Ki chính là phần dài nhất mà khi nhìn từ trái sang phải và từ phải sang trái của chuỗi Si đều như nhau, nói cách khác trên chuỗi Si ký tự đầu tiên bằng ký tự cuối, ký tự thứ hai bằng ký tự kề cuối, ..., ký tự thứ Ki bằng ký tự thứ i-Ki. Để xác định Ki ta dùng vòng lặp while tăng dần giá trị của Ki khi nào điều kiện s[1+k]=s[i-k] còn đúng: k:=0; while (s[1+k]=s[i-k]) do inc(k); Chương trình: const finp='dayk.inp'; fout='dayk.out'; 35
  • 36. var n, i, k: longint; s: string; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(n); readln(s); for i:=1 to n do begin k:=0; while (s[1+k]=s[i-k]) do inc(k); write(k,' '); end; close(input); close(output); end. TÌM SỐ ÂM LỚN NHẤT (Bài 3 – Tuyển sinh 2007 - 2008) Cho một dãy gồm N số nguyên a1, a2, …, aN, mỗi số có giá trị tuyệt đối không vượt quá 105 . Yêu cầu: Hãy tìm số âm lớn nhất X trong dãy. Dữ liệu: Vào từ file văn bản SOAM.INP: • Dòng đầu tiên chứa số nguyên dương N (1 ≤N ≤105 ). • N dòng tiếp theo, dòng thứ i chứa số ai. Kết quả: Ghi ra file văn bản SOAM.OUT trên một dòng duy nhất số X tìm được. Trong trường hợp không có lời giải, ghi ra số 0. Ví dụ: SOAM.INP SOAM.OUT 5 -4 3 2 -5 7 -4 Lời giải: Ta đọc qua các số của dãy và tìm số lớn nhất, tuy nhiên chỉ các số âm mới được xét đến. Ta dùng thêm một cờ để kiểm tra trong dãy có tồn tại số âm hay không. 36
  • 37. if (x<0) then {chỉ xét số âm} begin co:=true; {đánh dấu cờ là có tồn tại số âm} if (x>kq) then kq:=x; {cập nhật với kết quả lớn nhất} end; Chương trình: const finp='soam.inp'; fout='soam.out'; var n, kq, i, x: longint; co: boolean; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); co:=false; readln(n); kq:=-maxlongint; for i:=1 to n do begin readln(x); if (x<0) then begin co:=true; if (x>kq) then kq:=x; end; end; if co then writeln(kq) else writeln(0); end. DÃY HÌNH CHỮ NHẬT LỒNG NHAU (Bài 4 – Tuyển sinh 2007 - 2008) Trên mặt phẳng tọa độ cho N hình chữ nhật với các cạnh song song với hệ trục tọa độ, các hình chữ nhật được đánh số từ 1 tới N. Hình chữ nhật thứ i được cho bởi 4 số nguyên dương xi1, yi1, xi2, yi2, trong đó (xi1, yi1) là tọa độ đỉnh trái dưới, còn (xi2, yi2) là tọa độ đỉnh phải trên. Ta nói rằng hình chữ nhật thứ I nằm trong hình chữ nhật thứ j nếu trên mặt phẳng tọa độ, mọi điểm của hình chữ nhật i đều thuộc hình chữ nhật j. Yêu cầu: Với N hình chữ nhật cho trước, hãy tìm K hình chữ nhật với chỉ số i1, i2, …, iK sao cho hình i1 nằm trong hình i2, hình i2 nằm trong hình i3, …, hình iK-1 nằm trong hình iK và K là lớn nhất. Biết rằng hai hình chữ nhật bất kì trong N hình chữ nhật đã cho hoặc rời nhau hoặc một trong hai hình nằm trong hình còn lại. Dữ liệu: Vào từ file văn bản HCN.INP: 37
  • 38. • Dòng đầu tiên chứa số nguyên dương N (1 ≤N ≤100). • N dòng tiếp theo, dòng thứ i chứa 4 số nguyên dương xi1, yi1, xi2, yi2. Kết quả: Ghi ra file văn bản HCN.OUT số K tìm được. Ví dụ: HCN.INP HCN.OUT 3 1 1 7 4 3 1 6 6 2 2 5 4 2 Lời giải: Bước 1, ta sắp xếp các hình chữ nhật theo thứ tự giảm dần của diện tích. Điều này đảm bảo một hình chữ nhật chỉ có thể bị bao bởi một hình chữ nhật xuất hiện phía trước nó. Đoạn chương trình sau thực hiện việc sắp xếp: for i:=1 to n-1 do for j:=i+1 to n do if (dientich(h[i])<dientich(h[j])) then begin tam:=h[i];h[i]:=h[j];h[j]:=tam; end; Giả sử các hình chữ nhật đã được sắp xếp là h1, h2, ..., hn. Ta gọi f[i] là độ dài lớn nhất của chuỗi hình chữ nhật lồng nhau kết thúc tại hình hi. Để tính f[i], ta tìm vị trí j lớn nhất sao cho j < i là hình chữ nhật hj bao hình chữ nhật hi, khi đó f[i]=f[j]+1. Nếu không tồn tại vị trí j thì hình chữ nhật hi không bị bao bởi hình chữ nhật nào và f[i]=1: f[i]:=1; {khởi tạo giá trị ban đầu cho f[i]} for j:=i-1 downto 1 do if bao(h[j],h[i]) then {j là vị trí lớn nhất mà hình chữ nhật h[j] bao hình chữ nhật h[i]} begin f[i]:=f[j]+1; {tính f[i] theo f[j]} break; {thoát khỏi vòng lặp} end; if (f[i]>kq) then kq:=f[i]; {cập nhật lại kết qủa} Chương trình: const finp='hcn.inp'; fout='hcn.out'; MAXN=105; type hcn=record x1,y1,x2,y2: longint; end; var h: array[1..MAXN] of hcn; f: array[1..MAXN] of longint; i,j,n,kq: longint; 38
  • 39. tam:hcn; function dientich(a:hcn):longint; begin with a do dientich:=(x2-x1)*(y2-y1); end; function bao(a,b:hcn):boolean; begin bao:=(a.x1<=b.x1) and (b.x2<=a.x2) and (a.y1<=b.y1) and (b.y2<=a.y2); end; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(n); for i:=1 to n do with h[i] do readln(x1,y1,x2,y2); for i:=1 to n-1 do for j:=i+1 to n do if (dientich(h[i])<dientich(h[j])) then begin tam:=h[i];h[i]:=h[j];h[j]:=tam; end; kq:=0; for i:=1 to n do begin f[i]:=1; for j:=i-1 downto 1 do if bao(h[j],h[i]) then begin f[i]:=f[j]+1; break; end; if (f[i]>kq) then kq:=f[i]; end; writeln(kq); close(input); close(output); end. Năm học 2008-2009 Bài 1: Mã hoá 39
  • 40. Để quản lý tốt các hồ sơ trong kỳ thi tuyển sinh, hội đồng tuyển sinh trường PTNK đã quyết định đánh số các hồ sơ theo một phương pháp khoa học. Mã hồ sơ của thí sinh là một chuỗi gồm 10 chữ số. Tuy nhiên không phải bất kỳ chuỗi 10 chữ số nào cũng là mã hồ sơ hợp lệ bởi vì hội đồng tuyển sinh đưa ra một quy định ràng buộc chặt chẽ cho các chữ số đó. Nếu M=a1a2..a10 là một mã hồ sơ thì M phải thỏa mãn ràng buộc: Nếu đặt S(M)=1a1+2a2+3a3+…+10a10 thì S(M) phải là một số chia hết cho 11. Nhờ quy định này, trong những trường hợp do sơ xuất có một chữ số trong mã hồ sơ bị mờ, không đọc được thì ta vẫn có thể xác định được giá trị của nó. Ví dụ như: (quy ước ? là chữ số bị mờ): • Với M=00000000?1 thì có thể suy ra chữ số bị mờ là 5 vì theo ràng buộc, để S(M) là một số chia hết cho 11 nó chỉ có thể có giá trị là 55. • Tương tự với M=00000001?1 thì có thể suy ra chữ số bị mờ là 9. • Tương tự với M=00722?0858 thì có thể suy ra chữ số bị mờ là 6. Yêu cầu: Hãy viết chương trình giúp hội đồng tuyển sinh suy ra được chữ số bị mờ trong mã hồ sơ. Dữ liệu: Vào từ file văn bản ENCODE.INP có chứa mã hồ sơ có 1 chữ số bị mờ được thay bằng dấu chấm hỏi. Kết quả: Ghi ra file văn bản ENCODE.OUT chứa giá trị của chữ số bị mờ trong mã hồ sơ đã cho. Ví dụ: ENCODE.INP 00000000?1 ENCODE.OUT 5 00000001?1 9 00722?0858 6 Lời giải: Ta thử thay từng chữ số từ 0 đến 9 vào vị trí "?" và tính xem điều kiện S(M) chia hết cho 11 có được thoả mãn hay không như đoạn chương trình sau đây: for i:=1 to 10 do {r là tổng các số hạng của S(M) trừ vị trí có dấu ‘?’} if (s[i]<>'?') then r:=(r+i*(ord(s[i])-ord('0'))) mod 11; i:=pos('?',s); {i là vị trí của dấu ‘?’} for d:=0 to 9 do {duyệt qua tất cả các chữ số từ 0 đến 9} if (r+i*d) mod 11 = 0 then {kiểm tra xem S(M) có chia hết cho 11} begin writeln(d); {d chính là chữ số cần tìm} break; {thoát khỏi vòng lặp} end; Chương trình: 40
  • 41. const finp='encode.inp'; fout='encode.out'; var s: string; i, r, d: longint; c: char; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(s); for i:=1 to 10 do if (s[i]<>'?') then r:=(r+i*(ord(s[i])-ord('0'))) mod 11; i:=pos('?',s); for d:=0 to 9 do if (r+i*d) mod 11 = 0 then begin writeln(d); break; end; close(input); close(output); end. Bài 2: Biến đổi dãy số Cho dãy các số nguyên khác nhau đôi một a gồm n số a1, a2, ..., an và dãy số nguyên b gồm n số b1, b2, ..., bn. Trên dãy số a ta có thể áp dụng phép biến đổi T(i) thực hiện phép hoán vị giá trị hai phần tử ai và ai+1 (0<i<n) .Vấn đề đặt ra là có tồn tại hay không một dãy các phép biến đổi Ti1, Ti2, …, Tik sao cho khi áp dụng, dãy a ban đầu sẽ biến thành dãy b. Ví dụ nếu dãy a là 1, 3, 2, 4 và dãy b là 2, 1, 3, 4 thì ta có thẻ sử dụng dãy 2 phép biến đổi T(2) và T(1) để biến đổi a thành b. Yêu cầu: Cho hai dãy số a, b . Hãy chỉ ra một dãy các phép biến đổi a thành b hoặc cho biết không tồn tại dãy biến đổi như vậy. Dữ liệu: Vào từ file văn bản EXARRAY.INP gồm 3 dòng: • Dòng đầu tiên chứa số nguyên n là số lượng phần tử của mỗi dãy số (n<=100). • Dòng thứ hai chứa n số nguyên đôi một khác nhau ứng với các phần tử của dãy số a. • Dòng cuối cùng chứa n số nguyên ứng với các phần tử của dãy số b. Các số trong 2 mảng a và b đều có giá trị nằm trong đoạn [-10000,10000]. Kết quả: ghi ra file văn bản EXARRAY.OUT 41
  • 42. • Nếu không thể biến đổi a thành b, file chứa số duy nhất số -1. • Trong trường hợp ngựoc lại, file sẽ gồm 2 dòng: o Dòng đầu tiên chứa số nguyên k là số lượng phép biến đổi cần áp dụng. o Dòng thứ hai chứa k số nguyên duơng i1, i2, …, ik ứng với các phép biến đổi Ti1, Ti2, …, Tik tìm được. Hai số liên tiếp trên cùng một dòng được ghi cách nhau bởi một dấu cách. Ví dụ: EXARRAY.INP 4 1 3 2 4 2 1 3 4 EXARRAY.OUT 2 2 1 EXARRAY.INP 1 5 2 EXARRAY.OUT -1 Lời giải: Thuật toán của ta đơn giản như sau: tìm b[1] trong dãy a rồi di chuyển lên đầu dãy a, sau đó lại tìm b[2] rồi di chuyển lên vị trí thứ hai của dãy a, ... Nếu tại một bước ta không tìm thấy số tương ứng trong dãy a thì in ra -1. Đoạn chương trình sau thể hiện thuật toán: for i:=1 to n do {duyệt qua tất cả các phần tử của dãy b} begin vt[i]:=0; {vt[i] là vị trí xuất hiện của b[i] trong dãy a} for j:=i to n do {duyệt qua các phần tử của dãy a để tìm số b[i]} if (a[j]=b[i]) then {tìm đặt b[i]} begin vt[i]:=j; {cập nhật lại vt[i]} break; {thoát khỏi vòng lặp} end; if (vt[i]<>0) then {nếu tìm được số b[i]} begin kq:=kq+vt[i]-i; {cần thực hiện thêm vt[i]-i phép biển đổi để đưa số từ vị trí vt[i] về vị trí i} 42
  • 43. for j:=vt[i]-1 downto i do trao(j) {thực hiện biến đổi!} end else begin {nếu không tìm được số b[i]} writeln(-1); {in ra -1} exit; {thoát khỏi thủ tục} end; end; Chương trình: const MAXN=102; finp='exarray.inp'; fout='exarray.out'; var n: longint; a,b:array[1..MAXN] of longint; procedure nhap; var i: longint; begin readln(n); for i:=1 to n do read(a[i]); for i:=1 to n do read(b[i]); end; var kq: longint; vt: array[1..MAXN] of longint; procedure trao(i: longint); var tam: longint; begin tam:=a[i]; a[i]:=a[i+1]; a[i+1]:=tam; end; procedure xuly; var i, j, tam: longint; begin for i:=1 to n do begin vt[i]:=0; for j:=i to n do if (a[j]=b[i]) then begin vt[i]:=j; break; end; if (vt[i]<>0) then begin kq:=kq+vt[i]-i; 43
  • 44. for j:=vt[i]-1 downto i do trao(j) end else begin writeln(-1); exit; end; end; writeln(kq); for i:=1 to n do for j:=vt[i]-1 downto i do write(j,' '); end; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); nhap; xuly; close(input); close(output); end. Bài 3: Biến đổi bảng Xét bảng vuông gồm n dòng và n cột. Các dòng được đánh số từ 1 đến n từ trên xuống dưới. Các cột được đánh số từ 1 đến n từ trái sang phải. Ô nằm ở vị trí dòng i và cột j của bảng được gọi là ô (i,j). Trên bảng A đã cho, khoảng cách từ ô (i,j) đến ô (p,q) được tính bằng |i-p|+|j-q|. Tại ô (i,j) của bảng A ghi số nguyên không âm aij, i=1,2,…,n; j=1,2,..,n. Dựa vào các số được ghi trên bảng A, người ta cần xây dựng một bảng B cùng kích thước với A mà trên đó ô (i,j) của bảng B sẽ được ghi số bij xác định như sau: • Nếu aij > 0 thì bij = aij • Nếu aij = 0 thì bij có giá trị bằng giá trị apq của ô (p,q) gần ô (i,j) nhất trong số các ô có giá trị khác không trên dòng i và cột j của bảng A. Trong truờng hợp có nhiều ô khác không có cùng khoảng cách nhỏ nhất đến (i,j) thì ô (p,q) đựoc chọn là ô chứa số lớn nhất trong chúng. Nếu tất cả các phần tử của dòng i và cột j đều có giá trị 0 thì bij = 0. Yêu cầu: cho bảng A, hãy tìm bảng B. Dữ liệu: vào từ file văn bản NZTABLE.INP gồm: 44
  • 45. • Dòng đầu tiên ghi số nguyên dương n (n ≤ 50) • Dòng thứ i trong số n dòng tiếp theo ghi n số nguyên không âm ai1, ai2, …, ain là các số trên dòng thứ i của bảng, i=1,2,…,n; aij ≤ 10000. Kết quả: đưa ra file văn bản NZTABLE.OUT gồm n dòng, dòng thứ i ghi n số nguyên dương bi1, bi2, …, bin là các số trên dòng thứ i của bảng B. Hai số liên tiếp trên cùng một dòng được ghi cách nhau bởi một dấu cách. Ví dụ: NZTABLE.INP 4 1 0 3 0 4 0 0 5 0 0 6 0 0 0 0 0 NZTABLE.OUT 1 3 3 5 4 4 6 5 4 6 6 6 4 0 6 5 Lời giải: Ta xây dựng mảng b giống như định nghĩa của đề bài như đoạn chương trình sau: if (a[i,j]>0) then b:=a[i,j] else {biển b chỉ phần tử bij. Nếu aij > 0 thì bij = aij} begin b:=-maxlongint; {gán bij=-∞ vì ta cần giá trị bij lớn nhất} kc:=maxlongint; {kc là biến lưu khoảng cách, gán bằng ∞ vì ta cần giá trị nhỏ nhất} for p:=1 to n do {duyệt qua các ô trên cột j} if (a[p,j]<>0) then {chỉ xét các ô có giá trị khác 0} if ((abs(i-p)<kc) or {nếu ô có khoảng cách gần hơn} ((abs(i-p)=kc) and (a[p,j]>b))) {có khoảng cách bằng nhưng giá trị lớn hơn} then begin kc:=abs(i-p); {cập nhật lại khoảng cách} b:=a[p,j]; {cập nhật lại bij} end; 45
  • 46. for p:=1 to n do {duyệt qua các ô trên dòng i} ... {thực hiện tương tự} if (kc=maxlongint) then b:=0; {nếu tất cả các phần tử của dòng i, cột j đều bằng 0 thì bij=0} end; write(b,' '); {in bij ra màn hình} Chương trình: const MAXN=55; finp='NZTABLE.INP'; fout='NZTABLE.OUT'; var i, j, b, n, kc, p: longint; a: array[1..MAXN, 1..MAXN] of longint; begin assign(input, finp); reset(input); assign(output, fout); rewrite(output); readln(n); for i:=1 to n do for j:=1 to n do read(a[i,j]); for i:=1 to n do begin for j:=1 to n do begin if (a[i,j]>0) then b:=a[i,j] else begin b:=-maxlongint; kc:=maxlongint; for p:=1 to n do if (a[p,j]<>0) then if ((abs(i-p)<kc) or ((abs(i-p)=kc) and (a[p,j]>b))) then begin kc:=abs(i-p); b:=a[p,j]; end; for p:=1 to n do if (a[i,p]<>0) then if ((abs(j-p)<kc) or ((abs(j-p)=kc) and (a[i,p]>b))) then begin kc:=abs(j-p); b:=a[i,p]; end; if (kc=maxlongint) then b:=0; end; 46
  • 47. write(b,' '); end; writeln; end; close(input); close(output); end. Bài 4: Tìm mật khẩu Việc bảo vệ máy tính của mình để hạn chế người khác thâm nhập vào là một vấn đề đặt ra cho mọi nguời sử dụng máy tính. Để tăng tính an toàn trong lưu trữ, một nguời đã quyết định dấu mật khẩu truy cập máy tính của mình vào một xâu T với một quy ước sao cho khi cần anh ta có thể lấy lại đuợc mật khẩu từ T như sau. Là một người yêu thích số học anh ta thường chọn mật khẩu P là một số nguyên tố và đem dấu vào một xâu ký tự T sao cho P chính là số nguyên tố có giá trị lớn nhất trong số các số nguyên tố tạo được từ các xâu con của T (xâu con của một xâu ký tự T là một chuỗi liên tiếp các ký tự trong T). Ví dụ: xâu T=”Test1234#password5426” chứa mật khẩu là 23 vì T chứa các xâu con ứng với các số nguyên tố 2,3,23 và 5. Yêu cầu: Cho một xâu ký tự T chiều dài không quá 250 ký tự. Tìm mật khẩu P đã dấu trong xâu T biết P có giá trị nhỏ hơn 105 . Dữ liệu cho đảm bảo T chứa ít nhất 1 số nguyên tố. Dữ liệu: Vào từ file văn bản PASSWORD.INP gồm 1 dòng duy nhất là xâu T. Kết quả: Ghi ra file văn bản PASSWORD.OUT chứa số P tìm được. Ví dụ: PASSWORD.INP Test1234#password5426 PASSWORD.OUT 23 Lời giải: Ta duyệt qua tất cả các xâu con của xâu T mà có thể tạo thành một số và kiểm tra số đó có phải số nguyên tố hay không. Để duyệt qua các xâu con, ta duyệt qua vị trí đầu: for i:=1 to length(t) do {i là vị trí đầu của xâu con} Với mỗi vị trí đầu i, ta duyệt qua vị trí cuối của xâu con: j:=i; while (j<=length(t)) do begin ... inc(j); end; Có hai điều kiện để ta dừng quá trình duyệt một xâu con với vị trí đầu là i: 47
  • 48. • Gặp một ký tự không phải chữ số: if (t[j]<'1') or (t[j]>'9') then break; • Số tạo thành lớn hơn hoặc bằng 105 , vì đề bài đã nêu rõ P có giá trị nhỏ hơn 105 : if v>=100000 then break; Khi đọc được một chữ số mới, ta nhân số hiện tại với 10 rồi cộng thêm chữ số mới: v:=v*10+ord(t[j])-ord('0'); Nếu số thu được là số nguyên tố và lớn hơn kết quả tốt nhất tìm được thì cập nhật kết quả: if nguyento(v) then if (v>kq) then kq:=v; Chương trình: const finp='password.inp'; fout='password.out'; function nguyento(n: longint): boolean; var i: longint; begin nguyento:=false; if n<2 then exit; for i:=2 to trunc(sqrt(n)) do if (n mod i = 0) then exit; nguyento:=true; end; var t: string; i, kq, v, j: longint; begin assign(input,finp); reset(input); assign(output,fout); rewrite(output); readln(t); kq:=-1; for i:=1 to length(t) do begin v:=0; j:=i; while (j<=length(t)) do begin if (t[j]<'1') or (t[j]>'9') then break; v:=v*10+ord(t[j])-ord('0'); if v>=100000 then break; writeln(v); 48
  • 49. if nguyento(v) then if (v>kq) then kq:=v; inc(j); end; end; writeln(kq); close(input); close(output); end. 49