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:
-
Dòng đầu tiên chứa số nguyên dương N (1N100).
-
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])
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;
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])
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á
Để 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:
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 (0i1, 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
-
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:
-
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.
-
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}
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;
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:
-
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)
((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;
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)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)b))) then
begin
kc:=abs(j-p);
b:=a[i,p];
end;
if (kc=maxlongint) then b:=0;
end;
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:
-
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);
if nguyento(v) then
if (v>kq) then kq:=v;
inc(j);
end;
end;
writeln(kq);
close(input);
close(output);
end.
2>
Chia sẻ với bạn bè của bạn: |