You are on page 1of 32

Exercise programming language Pascal

Biên soạn: KS. Nguyễn Thanh Sang

(tập 1)

1
Bài 1: Xếp khách
Một khách sạn có N phòng đôi (phòng cho 2 người) được đánh số từ 1 đến
N. Khi có một đoàn khách đến thuê phòng, tiếp tân của khách sạn sẽ xếp
khách vào phòng tho nguyên tắc sau: mỗi cặp khách sẽ được xếp vào phòng
có chỉ số nhỏ nhất trong số các phòng trống. Nếu số lượng khách của đoàn
là số lẻ thì người cuối cùng của đoàn khách sẽ được xếp vào phòng có chỉ
số nhỏ nhất trong số các phòng còn trống. Nếu không còn phòng trống thì
số khách chưa có phòng sẽ được xếp tuần tự từng người một vào phòng có
chỉ số nhỏ nhất trong số các phòng mới có một khách (của đoàn khách
trước đó). Đầu tiên các phòng của khách sạn đều trống.
- Yêu cầu: cho trước trình tự đến của các đoàn khách và số lượng
khách của mỗi đoàn. Hãy xác định số lượng khách trong mỗi phòng của
khách sạn.
- Dữ liệu vào: file văn bản ROOMS.INP
+ Dòng đầu tiên chứa 2 số nguyên dương N (1<=N<=100) và G
được ghi cách nhau bởi dấu cách. N là số phòng của khách sạn, G là số
lượng đoàn khách.
+ Dòng thứ i trong số G dòng tiếp theo chứa số lượng khách của
đoàn khách thứ i (các đoàn khách được đánh số theo số thứ tự đến khách
sạn, bắt đầu từ 1).
Giả thiết rằng, không có 2 đoàn khách nào đến khách sạn cùng thời
điểm và tổng số khách của tất cả các đoàn không vượt quá sức chứa của
khách sạn (<= 2N).
- Kết quả: file văn bản ROOMS.OUT gồm N dòng. Dòng thứ i
chứa số lượng khách của phòng thứ i (1 <= i <= N).
- Ví dụ:
ROOMS.INP ROOMS.OUT
7 3 2
3 1
1 1
4 2
2
0
0
- Gợi ý và lời giải:
* Khi còn phòng trống thì xếp theo số phòng tăng dần, mỗi phòng
xếp đủ 2 người, nếu còn lẻ người cuối cùng thì xếp người đó vào 1 phòng.
* Khi hết phòng trống thì theo số hiệu phòng tăng dần xếp thêm
vào các phòng mới có 1 người cho đủ 2 người.

2
Chương trình dùng mảng 1 chiều P[1..N] of 0..2 để ghi nhận số
khách xếp vào các phòng. P[i]=0 là phòng i còn trống, P[i]=1 là phòng i có
1 người, P[i]=2 là phòng i có 2 người. Đồng thời dùng biến t để theo dõi
phòng cuối cùng đã có người (1 hoặc 2 người), dựa vào đó để xếp tiếp
khách vào phòng t+1. Khi hết phòng trống thì dùng biến t1 theo dõi phòng
có 1 người có chỉ số bé nhất để xếp nốt khách (từng người một) vào các
phòng mới có 1 người.
• Chương trình 1:
PROGRAM XepKhach;
USES Crt;
Const
max=1001;
Fi='Rooms.inp';
Fo='Rooms.out';
Var
P:array[0..max] of 0..2;
n,g,t,t1,i,x:integer;
F:text;
{----------------------}

BEGIN
clrscr;
assign(F,Fi); reset(F);
readln(F,n,g);
t:=0;
FillChar(P,SizeOf(P),0);
for i:=1 to g do
begin
readln(F,x);
while (t<n) and (x>1) do
begin
inc(t);
P[t]:=2;
dec(x,2);
end;
if (x=1) and (t<n) then
begin
inc(t);
P[t]:=1;
dec(x);
end;
if (t=n) and (x>0) then
begin
t1:=0;
while (P[t1]<>1) and (t1<n) do
begin
inc(t1);

3
if (P[t1]=1) and (x>0) then
begin
P[t1]:=2;
dec(x);
end;
end;
end;
end;
Close(F);
assign(F,Fo); Rewrite(F);
for i:=1 to n do Writeln(F,P[i]);
Close(F);
END.
• Chương trình 2:
PROGRAM XepKhach;
USES Crt;
Const
max=100;
Fi='Rooms.inp';
Fo='Rooms.out';
Var
P,D:array[1..max] of integer;
n,g,tongkhach:integer;
loi:boolean;
F:text;

procedure Nhap;
var k:integer;
begin
assign(F,Fi); reset(F);
Readln(F,n,g);
for k:=1 to g do Readln(F,D[k]);
Close(F);
FillChar(P,SizeOf(P),0);
end;

procedure Xuat;
var k:integer;
begin
Assign(F,Fo); Rewrite(F);
if loi then write(F,'Khach san qua suc chua!')
else
for k:=1 to n do Writeln(F,P[k]);
Close(F);
end;

function DemPhongTrong:integer;
var k,l:integer;
begin

4
l:=0;
for k:=1 to n do if P[k]=0 then l:=l+1;
DemPhongTrong:=l;
end;

procedure XepKhach(x:integer);
var k,l:integer;
begin
l:=DemPhongTrong;
if x<=0 then exit else
if x=1 then
begin
if l>0 then
for k:=1 to n do if P[k]=0 then begin P[k]:=x; break;
end;
if l=0 then
for k:=1 to n do if P[k]=1 then begin P[k]:=P[k]+x;
break; end;
end
else
begin
if l>0 then
for k:=1 to n do if P[k]=0 then begin P[k]:=2;
XepKhach(x-2); break; end;
if l=0 then
for k:=1 to n do if P[k]=1 then begin
P[k]:=P[k]+1;XepKhach(x-1); break; end;
end;
end;

procedure XuLy;
var k:integer;
begin
tongkhach:=0;
for k:=1 to g do tongkhach:=tongkhach+D[k];
if tongkhach<=n*2 then
begin
for k:=1 to g do XepKhach(D[k]);
loi:=false;
end
else
loi:=true;
end;

BEGIN
clrscr;
Nhap;
XuLy;
Xuat;

5
END.

Bài 2: Hình vuông


Cho một lưới N x N điểm gồm N dòng và N cột (2<=N<=9) là các điểm nút
của một lưới ô vuông (các dòng được đánh số từ trên xuống dưới, các cột
được đánh số từ trái qua phải, bắt đầu từ 1).
Trên lưới điểm đó cho một số đoạn thẳng,
mỗi đoạn nối một cặp điểm cạnh nhau trên
cùng một dòng (đoạn ngang) hoặc trên cùng
một cột (đoạn dọc). Cần phải đếm số hình
vuông với kích thước nhất định được tạo
thành bởi các đoạn thẳng đã cho của lưới
nêu trên. Chẳng hạn, ở hình 1 dưới đây có y
3: 2 hình kích thước 1 và 2 hình kích thước
2 (kích thước của hình vuông là số các đoạn
thẳng tạo thành 1 cạnh của hình vuông). y
- Yêu cầu: Hãy xác định số lượng các loại hình vuông và số hình
vuông mỗi loại trong lưới điểm đã cho (hình vuông có cùng kích thước
được xếp vào cùng loại.
- Dữ liệu vào: file văn bản SQUARE.INP
+ Dòng 1 chứa số nguyên N là số cột của lưới.
+ Dòng 2 chứa số nguyên M là số các đoạn thẳng được cho trên
lưới.
+ Mỗi dòng trong M dòng tiếp theo: Hịj chỉ một đoạn ngang trên
dòng thứ i nối 2 điểm ở cột j và j+1; Vji chỉ một đoạn dọc trên cột thứ j nối
2 điểm ở dòng i và i+1.
Số liệu được ghi từ vị trí đầu tiên của mỗi dòng, giữa ký tự và số và
giữa 2 số liên tiếp trên một dòng có đúng một dấu cách.
- Kết quả: file văn bản SQUARE.OUT
+ Dòng 1 ghi số nguyên P là số loại hình vuông có trên lưới.
+ Mỗi dòng trong P dòng tiếp theo ghi thông tin mô tả về một loại
hình vuông và số lượng hình vuông đó bao gồm 2 số nguyên a, b cho biết
có a hình vuông có cạnh độ dài b.
Các thông tin về các loại hình vuông phải được đưa ra theo thứ tự
tăng dần của độ dài cạnh. Trong trường hợp không tìm được bất kỳ hình
vuông nào thì file SQUARE.OUT chỉ gồm 1 dòng duy nhất chứa thông báo
“NO SQUARES”.
- Ví dụ:
SQUARE.INP SQUARE.OUT
4 2
6
16 21
H11 12
H13
H21
H22
H23
H32
H42
H43
V11
V21
V22
V23
V32
V41
V42
V43
- Gợi ý và lời giải:
* Trước hết, chuyển dữ liệu từ file vào 2 mảng 2 chiều
H[1..N,1..N], V[1..N,1..N] như sau: mảng H để theo dõi các đoạn nằm
ngang, các điểm [i,j] nằm trên một đoạn ngang liên tục thì H[i,j]=1, riêng
điểm cuối đoạn thì H[i,j]=2; mảng V để theo dõi các đoạn nằm dọc, các
điểm [i,j] nằm trên một đoạn dọc liên tục thì V[i,j]=1, riêng điểm cuối đoạn
thì V[i,j]=2.
* Ví dụ như hình trên ta có mảng H và V như sau:
H V
1102 1101
1112 2111
0120 0121
0112 0202
* Từ mảng H, xây dựng mảng R[1..N,1..N] để theo dõi từ mỗi
điểm (i,j) đi theo chiều sang phải thì có thể đi theo đoạn liên tục dài nhất là
bao nhiêu?
* Từ mảng V, xây dựng mảng D[1..N,1..N] để theo dõi từ mỗi
điểm (i,j) đi theo chiều xuống dưới thì có thể đi theo đoạn liên tục dài nhất
là bao nhiêu?
R V
2100 1303
3210 0212
0100 0101
7
0210 0000

* Tiếp theo, dựa vào các mảng R và D ta xây dựng mảng


KQ[1..50] với ý nghĩa sau: KQ[L]=s có nghĩa là loại hình vuông kích thước
bằng L có s hình.
Để có được mảng KQ, duyệt tất cả các điểm (i,j) có R[i,j]>0 và
D[i,j]>0 theo các bước:
- Tìm k1=min(R[i,j],D[i,j]);
- Xét các giá trị L=1,2,...,k1. Nếu D[i,j+sư>=L và R[i+s,j]>=L thì
bảo đảm có một hình vuông kích thước là L, nhận (i,j) là góc trái trên. Do
đó, tăng KQ[L] thêm 1 đơn vị.
Cuối cùng dựa vào mảng KQ, hiện kết quả theo yêu cầu đề bài.
* Chương trình:
PROGRAM HinhVuong;
USES crt;
CONST
max=50;
fi='SQUARE.INP';
fo='SQUARE.OUT';
TYPE
m1=array[1..max,1..max] of integer;
VAR
H,V,R,D:m1;
n:integer;
m:longint;
kq:array[1..max] of longint;
f:text;
{----------------------}
Procedure Nhap;
var
q,i,j:longint;
p:byte;
ch:char;
code:integer;
s1,s:string;
begin
fillchar(H,SizeOf(H),0);
fillchar(V,SizeOf(V),0);
assign(f,fi); reset(f);
readln(f,n);
readln(f,m);
for q:=1 to m do
begin
readln(f,s);
while (s<>'') and (s[1]=' ') do delete(s,1,1);

8
while (s<>'') and (s[length(s)]=' ') do
delete(s,length(s),1);
ch:=s[1];
delete(s,1,2);
p:=pos(' ',s);
if ch='H' then
begin
s1:=copy(s,1,p-1);
val(s1,i,code);
delete(s,1,p);
val(s,j,code);
H[i,j]:=1;
if H[i,j+1]=0 then H[i,j+1]:=2;
end
else
if ch='V' then
begin
s1:=copy(s,1,p-1);
val(s1,j,code);
delete(s,1,p);
val(s,i,code);
V[i,j]:=1;
if V[i+1,j]=0 then V[i+1,j]:=2;
end;
end;
end;
{----------------------}
Function ngang(i,j:integer):integer;
var l:integer;
begin
l:=1;
while (H[i,j]=1) and (j<n) do
begin
inc(l);
inc(j);
end;
if (H[i,j]=1) and (j=n) then inc(l);
ngang:=l;
end;
{-----------------}
procedure taoR;
var i,j,l,jj:integer;
begin
fillchar(R,SizeOf(R),0);
for i:=1 to n do
for j:=1 to n do
if (R[i,j]=0) and (H[i,j]=1) then
begin
l:=ngang(i,j)-1;

9
jj:=j;
R[i,jj]:=l;
while l>0 do
begin
dec(l);
inc(jj);
R[i,jj]:=l;
end;
end;
end;
{----------------------}
Function doc(i,j:integer):integer;
var l:integer;
begin
l:=1;
while (V[i,j]=1) and (i<n) do
begin
inc(l);
inc(i);
end;
if (V[i,j]=1) and (i=n) then inc(l);
doc:=l;
end;
{------------------}
procedure taoD;
var i,j,l,ii:integer;
begin
fillchar(D,SizeOf(D),0);
for i:=1 to n do
for j:=1 to n do
if (D[i,j]=0) and (V[i,j]=1) then
begin
l:=doc(i,j)-1;
ii:=i;
D[ii,j]:=l;
while l>0 do
begin
dec(l);
inc(ii);
D[ii,j]:=l;
end;
end;
end;
{----------------------}
function min(x,y:integer):integer;
begin
if x<y then min:=x else min:=y;
end;
{----------------------}

10
procedure dem;
var i,j,l,s:integer;
begin
fillchar(kq,SizeOf(kq),0);
for i:=1 to n do
for j:=1 to n do
if H[i,j]=1 then
begin
l:=min(R[i,j],D[i,j]);
for s:=1 to l do
if (D[i,j+s]>=s) and (R[i+s,j]>=s) then inc(kq[s]);
end;
end;
{------------------}
procedure Xuat;
var i,l: integer;
begin
assign(f,fo); Rewrite(f);
l:=0;
for i:=1 to max do
if kq[i]>0 then inc(l);
writeln(f,l);
for i:=1 to max do
if kq[i]>0 then writeln(f,kq[i],' ',i);
Close(f);
end;
{--------------------}

BEGIN
Nhap;
taoR;
taoD;
dem;
Xuat;
END.

Bài 3: Card mạng


Trung tâm máy tính NewAge dự trữ trong kho N card mạng được đánh số
hiệu từ 1 đến N (2<=N<=200). Định kỳ 6 tháng một lần, trung tâm tiến
hành kiểm tra chất lượng để xác định card mạng hỏng bằng cách thực hiện
K phép thử. Mỗi phép thử được thực hiện trên 1 cặp card mạng bất kỳ bằng
cách lắp chúng vào 2 máy tính và thử xác lập quan hệ giữa 2 máy đó. Nếu
các máy liên lạc được với nhau có nghĩa cả 2 card mạng chọn ra còn tốt,
trong trường hợp ngược lại - một trong 2 card mạng hoặc cả 2 đã bị hỏng.
Tuy nhiên, nhân viên kỹ thuật lại không phải là người thật chu đáo và cẩn
thận. Do vậy, anh ta đã chọn các cặp card mạng để tiến hành K phép thử
trên không theo một trình tự nào, thậm chí có những card mạng được thử đi
11
thử lại nhiều lần. Rất may là anh ta vẫn còn ghi lại kết quả cụ thể của từng
phép thử.
- Yêu cầu: theo kết quả K phép kiểm tra (0 <= K <=10.000), hãy
cho biết tình trạng của các card có thể xác định chính xác.
- Dữ liệu vào: file văn bản NETCARD.INP có
+ Dòng đầu tiên chứa 2 số nguyên N, K với N là số card mạng có
trong kho và K là số phép kiểm.
+ K dòng tiếp theo, trên mỗi dòng chứa 3 số nguyên I, J, V với I, J
tương ứng là số hiệu 2 card mạng được kiểm tra và V là kết quả kiểm tra
(V=1 khi hai card mạng đều tốt, trường hợp ngược lại V=0).
- Kết quả: file văn bản NETCARD.OUT chỉ 1 dòng duy nhất chứa
N số nguyên Q1, Q2, ..., QN với Qi xác định chất lượng của card mạng thứ i
(Qi = 1 - tốt; Qi = 0 - hỏng; Qi = 2 - chưa rõ)
- Ví dụ:
NETCARD.INP NETCARD.OUT
4 3 1 1 0 2
1 2 1
3 1 0
3 4 0
- Gợi ý và lời giải:
Dùng mảng một chiều A[1..N].
* Đọc file dữ liệu dòng 1 gán trị cho N và K
* Đọc K dòng tiếp theo, lần thứ nhất nếu gặp I, J, 1 thì gán A[I] =
A[J] = 1. Đọc lần thứ hai nếu gặp I, J, 0 thì: nếu A[I] = 1 thì ghi nhận A[J]
= 0, nếu A[J] = 1 thì ghi nhận A[I] = 0.
* Cuối cùng, ghi mảng A vào file kết quả.
* Chương trình:
Program CardMang;
Uses Crt;
Const
max=200;
fi='Netcard.inp';
fo='Netcard.out';
Var
A:array[1..max] of integer;
N,K:integer;
f:text;
{-------------}
Procedure Nhap_XuLy;
var
i,j,v,l:integer;
Begin
assign(f,fi); reset(f);
12
readln(f,N,K);
for l:=1 to N do A[l]:=2;
for l:=1 to K do
begin
readln(f,i,j,v);
if v=1 then
begin
A[i]:=1;
A[j]:=1;
end;
end;
reset(f); readln(f);
for l:=1 to K do
begin
readln(f,i,j,v);
if v=0 then
if A[i]=1 then A[j]:=0 else A[i]:=0;
end;
Close(f);
End;
{------------------}

Procedure Xuat;
var l:integer;
begin
assign(f,fo); Rewrite(f);
for l:=1 to N do write(f,A[l],' ');
Close(f);
end;
{------------------}

BEGIN
Nhap_XuLy;
Xuat;
END.

Bài 4: Thành lũy


Thời xa xưa, để phòng và chống lại sự tấn công của các bộ tộc khác, tù
trưởng bộ tộc Fladland đã quyết định cho xây dựng thành lũy quang các
điểm dân cư đông đúc của mình. Theo lời khuyên của thầy phù thủy, tên
của các thành lũy phải được chọn là một xâu con các ký tự liên tiếp nhau
của một tên thiêng W nào đó. Ví dụ, nếu W là ‘baobaab’ thì tên của thành
lũy có thể là ‘ba’, ‘oba’, ‘baab’, ... còn ‘bab’ hoặc ‘bob’ không thể được
dùng để đặt tên. Dĩ nhiên, các thành lũy khác nhau không được đặt tên
trùng nhau.
- Yêu cầu: Tù trưởng muốn biết có thể xây dựng được tối đa bao
nhiêu thành lũy dựa vào số tên có thể đặt.
13
- Dữ liệu vào: file văn bản BASTION.INP gồm một dòng chứa tên
thiêng W, trong đó, chỉ có các chữ cái latinh thường, có độ dài không quá
255 ký tự.
- Kết quả: file văn bản BASTION.OUT chứa một số nguyên là số
lượng các tên thành lũy có thể đặt khác nhau.
- Ví dụ:
BASTION.INP BASTION.OUT
baobaab 23
- Gợi ý và lời giải:
Giả sử xâu S là từ thiêng W của bộ tộc Fladland
For i := 1 to N do {i là độ dài xâu con}
Begin
S1 := S;
While length(S1) > 0 do
Begin
Chọn ra xâu con của S1 là S2 := copy(S1,1,i);
Xóa ký tự đầu của xâu S1;
Nếu S2 không có trong S1 thì S2 một xâu con phân biệt
{tăng biến đếm}
End;
End;
* Chương trình:
Program ThanhLuy;
Uses crt;
Const
fi='Bastion.inp';
fo='Bastion.out';
Var
fin,fout:text;
s,s1,s2:string;
l,i,j:word;
k:longint;
{-----------------------}
Procedure Dem;
var m:byte;
begin
k:=0;
for m:=1 to l do
begin
s1:=s;
while s1<>'' do
begin
if m>length(s1) then break;
s2:=copy(s1,1,m);

14
delete(s1,1,1);
if pos(s2,s1)=0 then inc(k)
end;
end;
writeln(fout,k);
end;
{-------------------------}
BEGIN
assign(fin,fi); reset(fin);
assign(fout,fo); rewrite(fout);
while not eof(fin) do
begin
readln(fin,s);
l:=length(s);
dem;
end;
Close(fin);
Close(fout);
END.

Bài 5: Chia bánh


Tại buổi sinh nhật của Tuấn có một cái bánh ga-tô hình tròn, bánh được
viền quanh bởi một loạt các quả dâu và nho. Một bạn gái bỗng đề xuất một
câu hỏi “Đố các bạn cắt bánh bằng một nhát dao thành 2 phần sao cho số
lượng quả dâu và nho trong phần bánh này bằng số lượng quả dâu và nho
trong phần bánh kia”
- Yêu cầu: bạn hãy lập trình giải câu đố trên.
- Dữ liệu vào: file văn bản CAKE.INP
+ Dòng đầu tiên ghi số n là số lượng quả trên bánh (n <= 255)
+ Dòng thứ hai ghi dãy gồm n ký tự là ‘D’ hoặc ‘N’, trong đó, ký
tự thứ i là ‘D’ nếu vị trí thứ i là quả dâu, là ‘N’ nếu ký tự thứ i là quả nho.
Các vị trí gắn quả trên bánh được đánh số từ 1 đến n theo chiều kim đồng
hồ bắt đầu từ một vị trí tùy ý.
- Kết quả: file văn bản CAKE.OUT chỉ 1 dòng duy nhất
+ Số -1 nếu không tìm được cách cắt thỏa mãn yêu cầu.
+ Ghi 2 số nguyên dương a, b (a < b) cho biết các quả ở vị trí a,
a+1, ..., b là các quả thuộc về cùng một trong 2 phần bánh.
- Ví dụ:
CAKE.INP CAKE.OUT CAKE.INP CAKE.OUT
6 3 5 5 -1
DNNNDN DNDDN
- Gợi ý và lời giải:

15
* Đọc file CAKE.INP, dùng mảng 1 chiều S[1..n] of char để ghi lại
các hạt viền quanh bánh. S[i] = ‘D’ là tại vị trí i có dâu, S[i] = ‘N’ là tại vị
trí i có nho. Đồng thời dùng biến Sd đếm số hạt dâu.
* Nếu n lẻ hoặc Sd lẻ thì kết quả ghi -1 vào file CAKE.OUT
* Ngược lại, tiến hành như sau: kiểm tra mọi phần bánh có chứa n
div 2 số hạt, nếu có đúng Sd div 2 hạt dâu thì đó là một phần bánh trong kết
quả (đây là bài toán duyệt mảng một chiều và đếm số phần tử thỏa một tính
chất nào đó).
Để kiểm tra mọi phần bánh có n div 2 số hạt ta chỉ cần kiểm tra các
đoạn hạt từ vị trí i đến j = i + n div 2-1, với i từ 1 đến n div 2.
* Chương trình:
Program ChiaBanh;
Uses Crt;
Const
fi='Cake.inp';
fo='Cake.out';
Var
S:array[1..256] of char;
f:text;
n,dau:integer;
{---------------------}
Procedure Nhap;
Var
ch:char;
i:integer;
Begin
assign(f,fi); reset(f);
readln(f,n);
for i:=1 to n do S[i]:='N';
for i:=1 to n do
begin
read(f,ch);
if ch='D' then inc(dau);
S[i]:=ch;
end;
Close(f);
End;
{-------------------}
Function CatDuoc(i,j:integer):boolean;
Var k,Sd,Sn:integer;
Begin
Sd:=dau div 2;
for k:=i to j do

16
if S[k]='D' then dec(Sd);
CatDuoc:=(Sd=0);
End;
{------------------}
Procedure XuLy;
Var i,j,c: integer;
Begin
assign(f,fo); rewrite(f);
if (n mod 2=1) or (dau mod 2=1) then
begin
write(f,-1);
Close(f);
halt;
end;
c:=n div 2;
for i:=1 to c do
begin
j:=i+c-1;
if CatDuoc(i,j) then
begin
write(f,i,' ',j);
Close(f);
halt;
end;
end;
write(f,-1);
Close(f);
End;
{---------------------}
BEGIN
dau:=0;
Nhap;
XuLy;
END.

Bài 6: Sắp xếp dãy số


Cho dãy số nguyên a1, a2, …, aN, với N <= 1.000
- Yêu cầu: Hãy tìm cách thực hiện một số ít nhất phép đổi chỗ 2 số
hạng bất kỳ của dãy để thu được dãy số mà số lẻ đứng ở vị trí lẻ, số chẵn
đứng ở vị trí chẵn.
- Dữ liệu vào: file văn bản DAYSO.INP
+ Dòng đầu tiên chứa số nguyên dương N.

17
+ Dòng thứ i trong số N dòng tiếp theo chứa số hạng ai của dãy số
đã cho (-32767 <= ai <= 32767, i = 1, 2, …, N).
- Kết quả: file văn bản DAYSO.OUT. Dòng đầu tiên ghi số lượng
phép đổi chỗ cần được thực hiện k (quy ước k = -1 nếu không thể biến đổi
được dãy đã cho thành dãy thỏa mãn yêu cầu đầu bài); nếu k > 0 thì dòng
thứ j trong số k dòng tiếp theo ghi chỉ số của 2 số hạng cần đổi chỗ cho
nhau ở lần đổi chỗ thứ j (j = 1, 2, …, k).
- Ví dụ:
DAYSO.INP DAYSO.OUT DAYSO.INP DAYSO.OUT
6 1 4 -1
1 5 6 1
2 3
3 2
4 5
6
5

- Gợi ý và lời giải:


* Duyệt mảng A (bằng vòng for i:=1 to N), nếu i là A[i] cùng tính
chẵn lẻ thì cho A[i] = 0, ngược lại nếu i và A[i] khác tính chẵn lẻ thì:
- Nếu A[i] chẵn thì cho A[i] = 2, tăng biến d2 là biến đếm số lượng
số lẻ đứng sai vị trí.
- Nếu A[i] lẻ thì cho A[i] = 1, tăng biến d1 là biến đếm số lượng số
lẻ đứng sai vị trí.
* Nếu d1 <> d2 thì ghi file kết quả là -1
* Nếu d1 = d2 thì phải thực hiện d1 phép đổi chỗ. Mỗi lần đổi chỗ,
tìm một số lẻ đứng sai vị trí đổi chỗ cho một số chẵn đứng sai vị trí.
* Chương trình :
Program SapXepDaySo;
Uses Crt;
Const
max=100;
fi='Dayso.inp';
fo='Dayso.out';
Var
A:array[1..max] of 0..2;
d1,d2,n:integer;
f:text;
{-----------------------}
procedure Nhap;
var i,x:integer;

18
begin
assign(f,fi); reset(f);
d1:=0; d2:=0;
readln(f,n);
for i:=1 to n do
begin
readln(f,x);
if x mod 2=0 then
if i mod 2=0 then A[i]:=0 else
begin
A[i]:=2;
inc(d2);
end;
if x mod 2=1 then
if i mod 2=1 then A[i]:=0 else
begin
A[i]:=1;
inc(d1);
end;
end;
Close(f);
end;
{------------------------}
procedure XuLy;
var i,j,k:integer;
begin
assign(f,fo); Rewrite(f);
if d1<>d2 then
begin
write(f,-1);
Close(f);
halt;
end;
writeln(f,d1);
for k:=1 to d1 do
begin
i:=1; j:=1;
while A[i]<>1 do inc(i);
while A[j]<>2 do inc(j);
if (A[i]=1) and (A[j]=2) then
begin
A[i]:=0; A[j]:=0;
if i<j then write(f,i,' ',j) else write(f,j,'
',i);

19
end;
end;
Close(f);
end;
{-------------------------}
BEGIN
Nhap;
XuLy;
END.

Bài 7: Thời điểm gặp mặt


Một nhóm N bạn học sinh của một lớp cùng tham gia một câu lạc bộ tin
học vào dịp nghỉ hè. Biết rằng khoảng thời gian mà bạn thứ i có mặt tại câu
lạc bộ là [ai,bi] (ai < bi) với ai, bi tương ứng là các thời điểm đến và rời khỏi
câu lạc bộ. Cô giáo chủ nhiệm lớp muốn tới thăm các bạn trong nhóm này.
Hãy giúp cô giáo chủ nhiệm lớp mốn tới thăm các bạn trong nhóm này.
- Yêu cầu: Hãy giúp cô giáo chủ nhiệm xác định thời điểm đến câu lạc
bộ sao cho tại thời điểm đó cô giáo gặp được nhiều bạn trong nhóm nhất.
- Dữ liệu vào: file văn bản MEETING.INP
+ Dòng đầu tiên ghi số nguyên dương N (N <= 1.000).
+ Dòng thứ i trong số N dòng tiếp theo ghi 2 số nguyên không âm
ai, bi (i = 1, 2, …, N).
- Kết quả: file văn bản MEETING.OUT
+ Dòng đầu tiên ghi số nguyên dương K là số bạn đang có mặt tại
câu lạc bộ tại thời điểm cô giáo đến.
+ Trong K dòng tiếp theo ghi chỉ số của K bạn có mặt ở câu lạc bộ
tại thời điểm cô giáo đến, mỗi dòng ghi chỉ số của một bạn.
- Ví dụ:
MEETING.INP MEETING.OUT MEETING.INP MEETING.OUT
6 3 5 1
1 2 1 1 2 1
2 3 2 3 5
2 5 3 7 9
5 7 11 15
6 7 17 21
9 11
- Gợi ý và lời giải:
* Cách 1: Dùng phương pháp đánh dấu trục số
Các điểm nguyên trên trục số tương ứng với các chỉ số của mảng 1
chiều m. Giả sử chỉ xét các thời điểm tính đến từng phút thì trong 1 ngày có
24*60 = 1440 thời điểm, do đó phải có mảng m[0..1440] of integer. Mảng
20
này sẽ để ghi nhận tại từng thời điểm tại câu lạc bộ có bao nhiêu bạn học
sinh. Ví dụ, m[400] = 3 có nghĩa là tại thời điểm 400 (phút) có 3 học sinh.
Đọc file MEETING.INP, mỗi lần đọc được khoảng thời gian [a, b]
của một bạn học sinh có mặt tại câu lạc bộ thì cho tăng m[i] một đơn vị
(với mọi i: a <= i <= b).
Công việc tiếp theo là tìm vị trí phần tử lớn nhất của mảng m. Giả
sử, vị trí đó là i0 thì m[i0] là số lượng học sinh nhiều nhất mà cô giáo có
thể gặp tại thời điểm i0 tại câu lạc bộ.
Một vấn đề còn lại là tìm xem cô giáo gặp được các bạn nào tại
thời điểm đó?
Chúng ta chỉ cần đọc file MEETING.INP một lần nữa, mỗi lần đọc
được khoảng thời gian [a, b] của một bạn học sinh có mặt tại câu lạc bộ,
nếu i0 thuộc [a, b] thì học sinh này gặp được cô giáo.
* Cách 2: Ta chỉ cần quan tâm đến thời điểm (đều là các số nguyên)
1- Thời điểm đến câu lạc bộ ai;
2- Thời điểm rời khỏi câu lạc bộ bi;
Sẽ được thay bằng (bi + 0.5).
Sắp xếp dãy 2*N các thời điểm này theo thứ tự tăng dần.
Duyệt i chạy từ 1 đến 2*N.
- Nếu thời điểm này nguyên, tức là có người mới tới thì tăng số
người có trong câu lạc bộ lên 1. Cập nhật tối ưu.
- Trường hợp ngược lại (có người rời câu lạc bộ) thì giảm số người
có trong câu lạc bộ đi 1.
* Chương trình:
Program ThoiDiemGapMat;
Uses Crt;
Const
fi='Meeting.inp';
fo='Meeting.out';
Var
m:array[0..1440] of integer;
f,g:text;
td,n,k:longint;
{--------------------}
Procedure Nhap;
var
a,b,j:integer;
i:longint;
begin
assign(f,fi); reset(f);
readln(f,n);
FillChar(m,SizeOf(m),0);
21
for i:=1 to n do
begin
readln(f,a,b);
for j:=a to b do inc(m[j]);
end;
Close(f);
end;
{---------------------}
Procedure Demk;
var
i,li:integer;
kmax:longint;
begin
kmax:=-1;
for i:=0 to 1440 do
if m[i]>kmax then
begin
kmax:=m[i];
li:=i;
end;
k:=kmax;
td:=li;
end;
{----------------------}
procedure Xuat;
var
a,b,i:integer;
begin
assign(f,fi); reset(f);
assign(g,fo); rewrite(g);
writeln(g,k);
readln(f,n);
for i:=1 to n do
begin
readln(f,a,b);
if (a<=td) and (td<=b) then writeln(g,i);
end;
Close(f);
Close(g);
end;
{-----------------------}
BEGIN
Nhap;
Demk;

22
Xuat;
END.

Bài 8: Trò chơi


Trò chơi bốc kẹo là trò chơi cho 2 đấu thủ. Người ta xếp N túi kẹo quang
một chiếc bàn tròn đánh số liên tiếp theo chiều kim đồng hồ bắt đầu từ 1 túi
kẹo bất kỳ. Túi kẹo thứ i có ai cái kẹo. Hai đấu thủ luân phiên thực hiện
nước đi, mỗi nước đi phải lấy một túi kẹo. Đối thủ thứ nhất là người thực
hiện nước đi đầu tiên, được chọn và lấy 1 trong N túi kẹo. Tiếp theo, đối
thủ đến lượt thực hiện nước đi phải chọn túi kẹo ở sát cạnh vị trí túi kẹo mà
đối thủ thực hiện nước đi ngay trước đó vừa lấy. Trò chơi kết thúc khi trên
mặt bàn không còn túi kẹo nào cả.
- Yếu cầu: cho biết chỉ số của túi kẹo mà đối thủ thứ nhất lấy đi
trong nước đi đầu tiên. Hãy tính tổng số kẹo lớn nhất mà đối thủ thứ hai có
thể lấy được khi tham gia trò chơi này.
- Dữ liệu vào: file văn bản GAME.INP
+ Dòng đầu tiên chứa số lượng túi kẹo là số N (1 <= N <= 1.000)
+ Dòng thứ 2 chứa số nguyên dương K là chỉ số của túi kẹo mà đấu
thủ thứ nhất chọn và lấy trong nước đi đầu tiên.
+ Dòng thứ i trong số N dòng tiếp theo chứa số nguyên dương ai
(ai<=32767), i = 1, 2, …, N.
- Kết quả: file văn bản GAME.OUT chứa tổng số kẹo lớn nhất của
đối thủ thứ 2 tìm được.
Ví dụ:
GAME.INP GAME.OUT
5 11
1
2
2
3
9
5
- Gợi ý và lời giải:
Trong cụm từ “vị trí ngay sát cạnh” nếu hiểu “vị trí” là vị trí cố
định ban đầu thì sau khi người thứ nhất bốc gói kẹo ở vị trí k, thì người thứ
hai có 2 khả năng chọn:
- Khả năng 1: Chọn gói ở vị trí k + 1 (nếu k + 1 = N + 1 thì chọn gói
1) và các gói tiếp theo tính theo chiều kim đồng hồ cách quãng từng gói một.

23
- Khả năng 2: Chọn gói ở vị trí k - 1 (nếu k - 1 = 0 thì chọn gói N)
và các gói tiếp theo tính theo chiều ngược chiều kim đồng hồ cách quãng
từng gói một.
Từ đó, suy ra tổng số kẹo người thứ hai lấy được nhiều nhất là số
lớn nhất trong hai số thu được tương ứng với 2 khả năng trên.
Tuy nhiên, có thể mở rộng bài toán này theo hướng sau: trong cụm
từ “vị trí ngay sát cạnh” có thể hiểu “vị trí” theo nghĩa tương đối, nghĩa là
sau mỗi lần bớt đi 1 gói kẹo thì 2 gói đang cách nhau (bởi gói kẹo bớt đi)
lại trở thành sát cạnh nhau. Hiểu như vậy thì đây là bài toán phức tạp, có
thể giải bằng đệ quy với kích thước bài toán không lớn lắm.
* Chương trình:
Program TroChoi;
Uses Crt;
Const
fi='Game.inp';
fo='Game.out';
max=1000;
Type
mang=array[0..max] of integer;
Var
f:text;
a,diem:mang;
n,bd:integer;
tong1,tong2:longint;
{----------------}
procedure Nhap;
var i:integer;
begin
assign(f,fi); reset(f);
readln(f,n);
readln(f,bd);
bd:=bd-1;
diem[bd]:=1;
for i:=0 to n-1 do readln(f,a[i]);
Close(f);
end;
{------------------}
procedure KhaNang1;
var i:integer;
begin
i:=(bd+1) mod n;
repeat
tong1:=tong1+a[i];

24
diem[i]:=1;
i:=(i+2) mod n
until diem[i]=1;
end;
{-------------------}
procedure KhaNang2;
var i:integer;
begin
FillChar(diem,SizeOf(diem),0);
diem[bd]:=1;
i:=(bd-1) mod n;
if i<0 then i:=n-abs(i);
repeat
tong2:=tong2+a[i];
diem[i]:=1;
i:=(i-2) mod n;
if i<0 then i:=n-abs(i);
until diem[i]=1;
end;
{-------------------}
procedure Xuat;
var max:longint;
begin
assign(f,fo); rewrite(f);
max:=tong2;
if tong1>tong2 then max:=tong1;
writeln(f,max);
Close(f);
end;
{------------------}
BEGIN
Nhap;
KhaNang1;
KhaNang2;
Xuat;
END.

Bài 9: Tam giác chuẩn


Cho một lưới tam giác đều vô hạn, có các điểm được đánh số tuần tự từ
trên xuống dưới, từ trái qua phải. Một tam giác đều có các đỉnh là các điểm
thuộc lưới gọi là tam giác chuẩn nếu các cạnh của tam giác tựa trên các
lưới. Ví dụ, {1, 2, 3} và {7, 8, 9} là các tam giác chuẩn, còn tam giác {6, 8,
14} và tam giác {1, 4, 5} không phải là các tam giác chuẩn.

25
1

2 3
5
4 6
7 10

11 15
16 21

22 28
29 36

- Yêu cầu: Cho chỉ số của 2 điểm thuộc lưới tam giác nói trên. Hãy
viết chương trình xác định 2 điểm đó có thể là đỉnh của một tam giác chuẩn
hay không. Trong trường hợp câu trả lời là có thì xác định chỉ số điểm thứ
3 để bộ 3 điểm ấy lập thành một tam giác chuẩn.
- Dữ liệu vào: file văn bản TAMGIAC.INP có cấu trúc như sau:
+ Dòng đầu tiên chứa N là số các cặp điểm cần xem xét.
+ Mỗi dòng trong N dòng tiếp theo chứa 2 số là chỉ số của cặp
điểm tương ứng đã cho, các số được đặt cách nhau một dấu cách.
- Kết quả: file văn bản TAMGIAC.OUT gồm N dòng tương ứng
với N cặp điểm đã cho. Mỗi dòng trong N dòng đó bắt đầu bằng số 1 hoặc
0 tùy theo hai điểm đã cho tương ứng có thể là hai đỉnh của một tam giác
chuẩn hay không. Trong trường hợp câu trả lời là có thì số tiếp theo là chỉ
số của đỉnh thứ ba tìm được. Các số cần đặt cách nhau một dấu cách.
- Ví dụ:
TAMGIAC.INP TAMGIAC.OUT
4 1 3
1 2 1 16
7 18 0
1 5 1 13
4 6
- Gợi ý và lời giải:
* Coi mặt phẳng chứa tam giác như một mặt phẳng tọa độ, góc tọa
độ O(0, 0) là đỉnh trên của tam giác (đỉnh có chỉ số 1). Trục hoành là trục
Oi (chứa cạnh trái tam giác), trục tung là trục Oj (chứa cạnh phải của tam
giác). Cần xây dựng 2 chương trình con CTC1 và CTC2:
26
- CTC1: chuyển chỉ số của một điểm lưới thành tọa độ. Ví dụ, điểm
7 = (3, 0), 8 = (2, 1), 9 = (1, 2), 25 = (3, 3) …
- CTC2: làm nhiệm vụ ngược lại, khi biết tọa độ của một điểm
lưới, tìm chỉ số của điểm lưới này.
Giả sử, đọc từ file dữ liệu được 2 chỉ số của một cặp điểm là x và y.
Không mất tính tổng quát, giả sử x < y. Dùng CTC1 có x = (j1, i1) và y =
(j2, i2), (i1, i2 là tung độ; j1, j2 là hoành độ). Xảy ra 3 tình huống:
- Nếu i1 = i2 (2 điểm x và y nằm trên 2 đường thẳng song song với
Oj) thì điểm thứ 3 cần tìm là z = y + j2 + j1.
- Nếu j1 = j2 (2 điểm x và y nằm trên 2 đường thẳng song song với
Oi) thì điểm thứ 3 cần tìm là z = y - (i2 - i1).
- Nếu i1 + j1 = i2 + j2 (2 điểm x và y nằm trên đường thẳng nằm
ngang cùng phương đường thẳng nối điểm 11 với điểm 15 chẳng hạn) thì
điểm thứ 3 có tọa độ là (i2, j1). Dựa vào CTC2 sẽ tìm được z.
X=(i1, j1) X=(i1, j1) X=(i1, j1) Y=(i2, j2)

Y=(i2, j2) Z=? Z=? Y=(i2, j2) Z=?

* Chương trình:
Program TamGiacChuan;
Uses Crt;
Const
fi='Tamgiac.inp';
fo='Tamgiac.out';
max=200;
Var
f1,f2:text;
x,y,z:longint;
d,c,i1,i2,j1,j2:integer;
{------------------------}
procedure CTC1(x:longint; var d,c:integer);
var i,j:integer;
p:longint;
Begin
i:=0; j:=0; p:=1;
while p<x do
begin
inc(j);
27
p:=p+j;
end;
if p>x then
begin
p:=p-j;
dec(j);
end;
while p<>x do
begin
p:=p+1;
dec(j);
inc(i);
end;
d:=i;
c:=j;
End;
{----------------------}
Function CTC2(x,y:integer):longint;
var j:integer;
p:longint;
Begin
j:=0; p:=1;
while j<x+y do
begin
inc(j);
p:=p+j;
end;
CTC2:=p+x;
End;
{---------------------}
procedure TimZ(x,y:integer; var z:longint);
Begin
CTC1(x,i1,j1);
CTC1(y,i2,j2);
if i1=i2 then z:=y+(j2-j1) else
if j1=j2 then z:=y-(i2-i1) else
if i1+j1=i2+j2 then z:=CTC2(i2,j1) else z:=0;
End;
{---------------------}
procedure XuLy;
procedure HoanVi(var x,y:longint);
var coc:longint;
begin
coc:=x;

28
x:=y;
y:=coc;
end;
Begin
assign(f1,fi); reset(f1);
assign(f2,fo); rewrite(f2);
readln(f1);
while not eof(f1) do
begin
readln(f1,x,y);
if x>y then HoanVi(x,y);
TimZ(x,y,z);
if z>0 then writeln(f2,1,' ',z) else writeln(f2,0);
end;
Close(f1);
Close(f2);
End;
{-------------------------}
BEGIN
XuLy;
END.

Bài 10: Thuê đất ở khu công nghiệp


Một khu công nghiệp hình chữ nhật có kích thước là M x N (0 < M, N),
công ty X muốn thuê một lô đất hình chữ nhật có một chiều là A và một
chiều là B đơn vị. Đương nhiên không thể thuê một lô mà trong đó có ô đã
được các công ty khác thuê từ trước.
- Yêu cầu: Lập trình tìm mãnh đất thỏa mãn yêu cầu.
- Dữ liệu vào: file văn bản THUEDAT.INP có cấu trúc như sau:
+ Dòng đầu chứa 5 số M, N, A, B, K lần lượt là số hangfm cột của
khu công nghiệp, kích thước các cạnh của khu đất mà công ty X muốn thuê
và số lô đất đã bị các công ty khác thuê trước đó (K < 15). Các số này cách
nhau một dấu cách.
+ Nếu K > 0 thì mỗi dòng trong K dòng tiếp theo là thông tin về một
lô đất đã thuê trước đó, bao gồm 4 số lần lượt là chỉ số hàng, chỉ số cột của ô
góc trên trái, chỉ số hàng và chỉ số cột của góc dưới phải của lô tương ứng.
- Kết quả: file văn bản THUEDAT.OUT có từ 1 tới 2 dòng:
+ Dòng đầu chứa số 1 hoặc 0 tùy theo bài toán có lời giải hay không.
+ Trong trường hợp có lời giải thì dòng thứ 2 sẽ có 4 số nguyên là
chỉ số hàng, chỉ số cột của ô góc trên trái, chỉ số hàng và chỉ số cột của ô
góc dưới phải của lô đất tìm được.
- Ví dụ:
29
THUEDAT.INP THUEDAT.OUT
6 9 4 2 3 1
1 3 2 5 1 1 4 2
5 2 5 2
4 7 6 9
- Gợi ý và lời giải:
* Dùng mảng 2 chiều H[0..M + 1, 0..N + 1] để biểu diễn khu đất
hình chữ nhật M x N ô (M dòng, N cột). Những ô (i, j) thuộc một lô đã thuê
thì H[i. j] = 1, chưa thuê thì H[i, j] = 0. Ngoài ra, nên viền xung quanh khu
đất bởi những ô “hàng rào” coi như đã có người thuê: H[0, j] = H[M + 1, j]
= 1 với mọi j = 0, 1, …, N + 1, H[i, 0] = H[i, N + 1] = 0 với mọi i = 1, 2,
…, M
Sau đó, tại mỗi ô (i, j) có H[i, j] = 0, kiểm tra trong các hình chữ
nhật có 2 kích thước a và b (nhận ô (i, j) là góc trên trái) có ô nào đã thuê
hay chưa. Nếu chưa có ô nào đã thuê thì được lô đất cần tìm.
Chú ý: a có thể là chiều ngang nhưng cũng có thể là chiều dọc của
lô đất cần tìm.
* Chương trình:
Program ThueDatKhuCongNghiep;
Uses Crt;
Const
fi='Thuedat.inp';
fo='Thuedat.out';
max=61;
Var
h:array[0..max,0..max] of 0..1;
m,n,a,b,k:byte;
li,lj,sn,kl:byte;
ok:boolean;
f:text;
{------------------}
procedure Nhap;
var i,j,sd,c,d,e,g:byte;
Begin
assign(f,fi); reset(f);
readln(f,m,n,a,b,k);
for i:=0 to m+1 do
for j:=0 to n+1 do h[i,j]:=1;
for i:=1 to m do
for j:=1 to n do h[i,j]:=0;
for sd:=1 to k do
begin

30
readln(f,c,d,e,g);
for i:=c to e do
for j:=d to g do h[i,j]:=1;
end;
Close(f);
End;
{-------------------}
Function KiemTra1(i,j:byte):boolean;
var d,c:byte;
Begin
KiemTra1:=false;
for d:=i to i+a-1 do
for c:=j to j+b-1 do
if h[d,c]<>0 then exit;
KiemTra1:=true;
End;
{-------------------}
Function KiemTra2(i,j:byte):boolean;
var d,c:byte;
Begin
KiemTra2:=false;
for d:=i to i+b-1 do
for c:=j to j+a-1 do
if h[d,c]<>0 then exit;
KiemTra2:=true;
End;
{--------------------}
procedure Tim;
var i,j:byte;
Begin
sn:=0;
for i:=1 to m do
for j:=1 to n do
if h[i,j]=0 then
begin
kl:=0;
ok:=KiemTra1(i,j);
if ok then
begin
inc(sn);
kl:=1;
li:=i;
lj:=j;
exit;

31
end
else
begin
kl:=0;
ok:=KiemTra2(i,j);
if ok then
begin
inc(sn);
kl:=2;
li:=i;
lj:=j;
exit;
end;
end;
end;
End;
{----------------------}
procedure Xuat;
var i,j:byte;
Begin
assign(f,fo); rewrite(f);
if sn=0 then write(f,0) else
begin
writeln(f,1);
if kl=1 then write(f,li,' ',lj,' ',li+a-1,'
',lj+b-1)
else
if kl=2 then write(f,li,' ',lj,' ',li+b-1,'
',lj+a-1);
end;
Close(f);
End;
{---------------------}
BEGIN
Nhap;
Tim;
Xuat;
END.

32

You might also like