đề ôn tập olympic 304 cho học sinh trung học phổ thông, giáo viên, học sinh có thể tham khảo Câu 1. SUMMIN (6 điểm) Trên máy tính thời gian để thự hiện việc tính tổng của hai số nguyên dương là phụ thuộc vào giá trị của chúng. Giả sử khi cộng hai số nguyên dương x và y ta phải trả chi phí thời gian có giá trị bằng 5% giá trị của tổng hai số. Yêu cầu: Cho dãy A gồm N số nguyên dương a1, a2, ..., aN . Cần tìm cách tính tổng của các số này với tổng chi phí thời gian là nhỏ nhất. Input: SUMMIN.inp Dòng đầu tiên ghi số nguyên dương N (2 N 15000) Dòng tiếp theo ghi N số nguyên dương mà ta cần tính tổng, hai số liên tiếp được ghi cách nhau bởi ít nhất một dấu cách ( ai 105 , i =1..N). Output: SUMMIN.out Gồm một dòng ghi tổng chi phí theo cách thực hiện tính tổng tìm được. Kết quả được ghi với hai chữ số sau dấu chấm thập phân.
Trang 1ĐỀ 01 Câu 1 SUMMIN (6 điểm)
Trên máy tính thời gian để thự hiện việc tính tổng của hai số nguyên dương là phụ thuộc vào giá trị của chúng Giả sử khi cộng hai số nguyên dương x và y ta phải trả chi phí thời gian có giá trị bằng 5% giá trị của tổng hai số
Yêu cầu: Cho dãy A gồm N số nguyên dương a1, a2, , aN Cần tìm cách tính tổng của các số này với tổng chi phí thời gian là nhỏ nhất
Input: SUMMIN.inp
- Dòng đầu tiên ghi số nguyên dương N (2 N 15000)
- Dòng tiếp theo ghi N số nguyên dương mà ta cần tính tổng, hai số liên tiếp được ghi cách nhau bởi ít nhất một dấu cách ( ai 105, i =1 N)
Output: SUMMIN.out
Gồm một dòng ghi tổng chi phí theo cách thực hiện tính tổng tìm được Kết quả được ghi với hai chữ số sau dấu chấm thập phân
Ví dụ:
SUMMIN.inp SUMMIN.out
2
1 1
0.10
5
1 7 4 9 2
2.35
HD:
- Để cộng N số lại với nhau, ta cần thực hiện các phép cộng (N-1 phép cộng)
- Nếu với mỗi phép cộng ta mất chi phí là bé nhất thì tổng N-1 phép cộng ta có tổng chi phí là bé nhất
- Sử dụng Heap_Min
-Tạo Heap_Min chứa n phần tử của dãy A: a1, a2, , aN
- Gọi Res là chi phí thời gian bé nhất cần tìm, khởi tạo
Res :=0;
- Lặp
Lấy ra hai phần tử bé nhất trong Heap_Min
x:=pop(1);
y:=pop(1);
Cộng chi phí tính tổng hai số x và y vào Res
Res:=Res+(x+y)*5/100;
Khi công hai số x và y sinh ra một giá trị mới (x+y), nạp giá trị (x+y) vào Heap_Min
push(x+y);
Cho đến khi Heap_Min còn 1 phần tử
Kết quả : Res
Trang 2Program SUMMIN;
Const fi='SUMMIN.inp'; fo='SUMMIN.out';
Var f,g:text;
Res:Extended;
top,N,K:longint;
a:array[0 15000] of longint;
q:array[0 15000] of int64;
procedure doicho(i,j:longint);
var tg:int64;
begin
tg:=q[i];
q[i]:=q[j];
q[j]:=tg;
end;
procedure Up_heap(i:longint);
begin
if (i=1) then exit;
if (q[i div 2]>q[i]) then
begin
doicho(i,i div 2);
up_heap(i div 2);
end;
end;
procedure down_heap(i:longint);
var j:longint;
begin
j:=i*2;
if j > top then exit;
if (j<top)and(q[j]>q[j+1]) then j:=j+1;
if(q[i]>q[j]) then
Trang 3up_heap(top);
end;
function pop(vitri:longint):int64;
begin
pop:=q[vitri];
q[vitri]:=q[top];
dec(top);
up_heap(vitri);
down_heap(vitri);
end;
Procedure Doc;
Var i,j:longint;
Begin
Assign(f,fi); reset(f);
Readln(f,N);
For i:=1 to N do Read(f,a[i]);
Close(f);
End;
Procedure Xuly;
Var i:Longint;
x,y:int64;
Begin
top:=0;
For i:=1 to N do push(a[i]);
Res:=0;
While top>1 do
Begin
x:=pop(1); y:=pop(1);
Res:=Res+(x+y)*5/100;
push(x+y);
End;
End;
Procedure ghi;
Var i,j:longint;
Begin
Assign(g,fo); Rewrite(g);
Writeln(g,Res:0:2);
Close(g);
End;
BEGIN
Trang 4doc;
xuly;
ghi;
END
Trang 5Câu 2 FGIRD (7 điểm)
Cho xâu S có độ dài 2N-1 và một lưới ô vuông A có kích thước NxN, mỗi ô trên lưới ghi một chữ cái Một người tìm cách di chuyển bắt đầu từ ô ở góc trên trái đến ô ở góc dưới phải, mỗi lần di chuyển chỉ được quyền sang ô có chung cạnh ở bên phải hoặc phía dưới sao cho các chữ cái trong các ô trên đường di chuyển tạo thành xâu S
Yêu cầu: Cho trước lưới ô vuông A và xâu S, hãy xác định số cách di chuyển thỏa mãn
yêu cầu đặt ra
Input: FGIRD inp
- Dòng đầu tiên ghi số N (2≤ N ≤1000);
- N dòng tiếp theo, mỗi dòng chứa N chữ cái Latin in thường (không nhất thiết phải khác nhau);
- Dòng cuối ghi xâu S gồm 2N-1 chữ cái Latin in thường
Output: FGIRD out
Gồm một dòng ghi số nguyên là phần dư của phép chia số cách di chuyển thỏa điều kiện cho 1000003
Ví dụ:
FGIRD inp FGIRD out
3 aaa aba baa aabaa
5
HD:
- Vị trí (i,j) trong bảng A tương ứng với vị trí (i+j)-1 trong xâu S
- Gọi L[i,j] là số cách di chuyển đến ô A[i,j] để thu được xâu con S[1 (i+j)-1]
- Khởi tạo L[0,1] = 1 và L[1,0] =0 hoặc L[0,1] = 0 và L[1,0] =1
- Công thức tính L[i,j]:
L[i,j] = 0 nếu a[i,j] <> s[i+j-1]
L[i,j] = L[i-1,j] + L[i, j-1] nếu a[i, j] = s[i+j-1]
- Kết quả L[n,n]
Chương trình tham khảo
const
fi='FGIRD.inp';
fo='FGIRD.out';
max=1000;
var f:text;
a:array[0 max] of ansistring;
s:ansistring;
n:longint;
Trang 6l:array[0 max,0 max] of longint;
procedure doc;
var i,j:longint;
begin
assign(f,fi);reset(f);
readln(f,n);
for i:=1 to n do readln(f,a[i]);
readln(f,s);close(f);
end;
procedure xuly;
var i,j:longint;
begin
L[0,1] := 1;
L[1,0] :=0;
for i:=1 to n do
for j:=1 to n do
if a[i][j]=s[i+j-1] then l[i,j]:=(l[i,j-1]+l[i-1,j]) mod 1000003
else l[i,j]:=0;
end;
procedure ghi;
begin
assign(f,fo);rewrite(f);
write(f,l[n,n]);
close(f)
end;
begin
doc;
xuly;
ghi;
end
Trang 7Câu 3 FGRAPH( 7 điểm )
Sống tại một thành phố lớn có mật độ dân số cao và giao thông phức tạp, ông Bình rất lo về nạn kẹt xe Để thuận tiện cho việc đi lại, ông Bình đang tìm hiểu các con đường ngắn nhất dẫn từ nhà đến cơ quan Bạn hãy giúp ông Bình thực hiện công việc kho khăn này
Bản đồ thành phố là gồm có N nút giao thông và M con đường hai chiều nối các nút giao thông này Độ dài của mỗi con đường là một số nguyên dương Nhà ông Binh ở nút giao thông 1, cơ quan ông bình ở nút giao thông N
Input: FGRAPH.inp
Dòng thứ nhất ghi hai số nguyên N và M (1 ≤ N ≤ 200, 1 ≤ M ≤ 20000)
M dòng tiếp theo, mỗi dòng ghi 3 số nguyên dương U, V, L ( con đường nối U đến V với độ dài L, L ≤ 32000)
Output: FGRAPH.out
Gồm một dòng ghi số lượng đường đi ngắn nhất
Ví dụ:
FGRAPH.inp FGRAPH.out
3 3
1 2 3
1 3 4
2 3 1
2
Thuật toán:
Áp dụng thuật toán Dijkstra
HD:
Gọi D[i] là độ dài đường đi ngắn nhất từ 1 đến i
Gọi Count[i] là số lượng đường đi ngắn nhất từ 1 đến i
Giả sử đã tìm được đường đi và số lượng đường đi ngắn nhất từ đỉnh 1 đến đỉnh u
Với mỗi đỉnh v là kề của u,
- Nếu đường đi 1 u v ngắn hơn đường đi 1 v
Nếu D[v]> D[u]+a[u,v] thì
+ D[v]=D[u]+a[u,v]
+ Count[v] = Count[u]
- Nếu đường đi 1 u v bằng đường đi 1 v
Nếu D[v]=D[u]+a[u,v] thì
+ L[v] = L[u] + L[v]
Count[n] là kết quả cần tìm
Chú ý: Luôn có đường đi từ 1 đến n
Chương trình tham khảo:
Trang 8Program FGRAPH;
const
fi='FGRAPH.inp';
fo='FGRAPH.out';
maxn=200;
maxm=20000;
vc=1000000000;
var n,m: longint;
f: text;
d: array [-1 maxn+4] of longint;
count: array [-1 maxn+4] of int64;
dd: array [-1 maxn+4] of boolean;
c: array [1 maxn+4,1 maxn+4] of longint;
procedure doc;
var i,j,u,v,t: longint;
begin
assign(f,fi); reset(f);
fillchar(c,sizeof(c),0);
readln(f,n,m);
for i := 1 to n do
for j := 1 to n do
if i = j then c[i, j] := 0
else c[i, j] := vc;
for i := 1 to m do
begin
Readln(f, u, v, t);
if c[u,v] > t then
begin
c[u,v] := t;
c[v,u] := t;
end;
Trang 9for i:=1 to n do d[i]:=vc;
d[1]:=0;
repeat
u := 0; min := vc;
for i := 1 to n do
if dd[i] and (d[i] < min) then
begin
min := d[i];
u := i;
end;
if (u = 0) then Break;
dd[u]:=false;
for v := 1 to n do
if dd[v] then
if (d[v] > d[u] + c[u, v]) then
begin
d[v] := d[u] + c[u, v];
count[v]:=count[u];
end
else if (d[v]=d[u]+c[u,v]) then count[v]:=count[v]+count[u];
until False;
end;
procedure ghi;
var i: longint;
begin
assign(f,fo); rewrite(f);
write(f,count[n]);
close(f);
end;
BEGIN
doc;
dijkstra;
ghi;
END
Trang 10ĐỀ 02 Bài 1:
Duyệt kết hợp chặt nhị phân
Bài 2: Bánh sinh nhật
Nhận xét: Giả sử x[i] là vị trí cuối cùng Bình dừng lại ăn bánh để đạt kết quả tối
ưu Ta luôn tốn 1 khoảng thời gian để đi từ 0 đến x[i], gọi thời gian đó là T0 Công việc còn lại cần làm là tìm cách ăn bánh sao cho số bánh ăn được là nhiều nhất Dễ dàng nhận thấy chỉ việc chọn ăn những bánh nhỏ nhất sao cho tổng thời gian ăn bánh + T0 <= T
Sử dụng cấu trúc Heap max để lưu trữ thời gian cần để ăn hết các bánh Duyệt trên trục toạ độ thử các vị trí i là vị trí cuối cùng Bình dừng lại ăn bánh, đi qua vị trí nào thì push hết bánh ở vị trí đó vào Heap, cập nhật lại Heap Chừng nào T0 + (tổng thời gian để ăn hết tất cả bánh có trong Heap) > T thì pop bánh có thời gian ăn lớn nhất ra Sau đó cập nhật lại kết quả tối ưu
Bài 3:
Phát biểu ngắn
Cho đồ thị có hướng có trọng số dương trên cung G=(V,E, C), hai đỉnh (s,t) và tập cạnh A={ (ui, vi), i=1,2, ,k}
Cần chọn cạnh e A sao cho việc bổ sung nó vào đồ thị G làm cho độ dài đường đi
từ s đến t giảm được nhiều
Thuật toán:
Tính d1(s,v) độ dài đường đi ngắn nhất từ s đến v, v V \ { s} (1)
Tính d2(v,t) độ dài đường đi ngắn nhất từ v đến t, v V \ { t} (2)
Tính
Kq1:=min { d1(s,u) + c(u,v) + d2(v,t) : (u,v) A}
Kq2: =min { d1(s,v) + c(u,v) + d2(u,t) : (u,v) A} (3)
Khi đó kq:=min(kq1,kq2, d1(s,t)) là đáp số cần tìm
CHƯƠNG TRÌNH THAM KHẢO
Chương trình đề nghị giải Bài 1
{$Mode ObjFPC}
Const fi='GROUP.INP';
fo='GROUP.OUT';
nMax=51;
Trang 11Assign(f,fi);
Reset(f);
Readln(f,n,k);
aMax:=0;
For i:=1 to n do
Begin
Read(f,a[i]);
aMax:=aMax+a[i];
End;
Close(f);
aMax:=aMax div k +1;
End;
Function Check(u:int64):boolean;
Begin
Sum:=0;
b:=a;
For i:=1 to n do
Begin
If b[i]>u then b[i]:=u;
Sum:=Sum+b[i];
End;
Exit(Sum>=u*k);
End;
Procedure Main;
Begin
Low:=0; High:=aMax;
While Low<High do
Begin
Mid:=(Low+High+1) div 2 ;
If check(Mid) then Low:=Mid else
High:=Mid-1;
End;
Res:=High;
End;
Procedure Print;
Begin
Assign(f,fo);
Rewrite(f);
Writeln(f,Res);
Close(f);
End;
Begin
Enter;
Trang 12Main;
Print;
ENd
Chương trình đề nghị giải Bài 2
Program ANNICAKE;
CONST FINP = 'ANNICAKE.INP'; FOUT = 'ANNICAKE.OUT';
VAR fi,fo:text;
num,N,res:longint;
Sum,TK:int64;
H,X,T:array[1 100005] of longint;
Procedure UpHeap(x:longint);
Var c,tmp:longint;
BEGIN
tmp := H[x];
c := x div 2;
while (c>0)and(T[H[c]]<T[tmp]) do
begin
H[x] := H[c];
x := c;
c := x div 2;
end;
H[x] := tmp;
END;
Procedure Add(x:longint);
BEGIN
inc(num);
H[num] := x;
Sum := Sum+T[x];
UpHeap(num);
END;
Procedure DownHeap(x:longint);
Var c,tmp:longint;
BEGIN
tmp := H[x];
Trang 13BEGIN
x := H[1];
H[1] := H[num];
dec(num);
DownHeap(1);
Sum := Sum-T[x];
END;
Procedure Process;
Var i,u:longint;
BEGIN
readln(fi,N,TK);
for i:=1 to N do readln(fi,X[i],T[i]);
for i:=1 to N do
begin
Add(i);
While((Sum+X[i])>TK)and(num>0) do Get(u);
if res < num then res := num;
end;
writeln(fo,res);
END;
BEGIN
assign(fi,FINP); assign(fo,FOUT);
Process;
close(fi);
close(fo);
END
Chương trình đề nghị giải Bài 3
Program TRANSNET;
CONST FINP = 'TRANSNET.INP'; FOUT = 'TRANSNET.OUT'; Ulti = trunc(1e9); maxN = 11001; maxM = 110001;
VAR fi,fo:text;
N,M,K,S1,T1,res:longint;
x,y,z:array[0 maxM] of longint;
head:array[1 2,0 maxN] of longint;
A,C:array[1 2,0 maxM] of longint;
num:array[1 2] of longint;
H,L,id:array[1 2,0 maxM] of longint;
dd:array[1 2,0 maxN] of boolean;
Procedure INIT;
Var i:longint;
BEGIN
readln(fi,N,M,K,S1,T1);
for i:=1 to M do
begin
Trang 14readln(fi,x[i],y[i],z[i]);
inc(head[1][x[i]]);
inc(head[2][y[i]]);
end;
for i:=1 to N+1 do
begin
head[1][i] := head[1][i-1] + head[1][i];
head[2][i] := head[2][i-1] + head[2][i];
end;
for i:= 1 to M do
begin
A[1][head[1][x[i]]] := y[i];
A[2][head[2][y[i]]] := x[i];
C[1][head[1][x[i]]] := z[i];
C[2][head[2][y[i]]] := z[i];
dec(head[1][x[i]]);
dec(head[2][y[i]]);
end;
END;
Procedure UpHeap(z,x:longint);
Var tmp,c:longint;
BEGIN
tmp := H[z][x];
c := x div 2;
while(c>0) and (L[z][tmp]<L[z][H[z][c]]) do
begin
H[z][x] := H[z][c];
id[z][H[z][x]] := x;
x := c;
c := x div 2;
end;
H[z][x] := tmp;
id[z][H[z][x]] := x;
END;
Procedure DownHeap(z,x:longint);
Var tmp,c:longint;
Trang 15x := c;
end;
H[z][x] := tmp;
id[z][H[z][x]] := x;
END;
Procedure Add(z,x:longint);
BEGIN
if(id[z][x]>0) then UpHeap(z,id[z][x])
else
begin
inc(num[z]);
H[z][num[z]] := x;
id[z][x] := num[z];
UpHeap(z,num[z]);
end;
END;
Procedure Get(z:longint;Var x:longint);
BEGIN
if (num[z]=0) then
begin
x := 0;
Exit;
end;
x := H[z][1];
H[z][1] := H[z][num[z]];
id[z][H[z][1]] := 1;
dec(num[z]);
DownHeap(z,1);
END;
Procedure Edit(z,u:longint);
Var i:longint;
BEGIN
for i:=head[z][u]+1 to head[z][u+1] do
if not dd[z][A[z][i]] then
begin
if (L[z][A[z][i]] > L[z][u] + C[z][i]) then
begin L[z][A[z][i]] := L[z][u] + C[z][i];
Add(z,A[z][i]);
end;
end;
END;
Procedure Dijkstra(z,S:longint);
Var i,u:longint;
Trang 16BEGIN
for i:=1 to N do L[z][i] := Ulti;
dd[z][S] := true;
L[z][S] := 0;
Add(z,S);
repeat
Get(z,u);
if (u=0) then break;
dd[z][u] := true;
Edit(z,u);
until(u=0);
END;
Function Cal(x,y,z:longint):longint;
Var S:longint;
BEGIN
if(L[1][x] = Ulti) or (L[2][y] = Ulti) then Exit(Ulti);
S := z + L[1][x] + L[2][y];
Exit(S);
END;
Procedure Process;
Var i,x,y,z,tmp:longint;
BEGIN
INIT;
res := Ulti;
Dijkstra(1,S1);
Dijkstra(2,T1);
if res > L[1][T1] then res := L[1][T1];
if res > L[2][S1] then res := L[2][S1];
for i:=1 to K do
begin
readln(fi,x,y,z);
tmp := Cal(x,y,z);
if res > tmp then res := tmp;
tmp := Cal(y,x,z);
if res > tmp then res := tmp;
end;
Trang 17END