Tải bản đầy đủ (.pdf) (17 trang)

DeOnTap OLP30 4 de01 02 solution (1)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (510.9 KB, 17 trang )

ĐỀ 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
0.10
11
5
2.35
17492
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
Chương trình tham khảo


Program 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
begin
doicho(i,j);
down_heap(j);
end;
end;
procedure push(giatri:int64);
begin
inc(top);
q[top]:=giatri;
2


up_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
3


doc;
xuly;
ghi;
END.

4


Câu 2. FGIRD (7 điểm)
Cho xâu S có độ dài 2N-1 và một lưới ơ vng 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
5
aaa
aba
baa
aabaa
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;
5



l: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.

6


Câ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
33
2
123
134
231
Thuật tố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ú ý: Ln có đường đi từ 1 đến n
Chương trình tham khảo:
7


Program 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;
end;
close(f);
end;
procedure dijkstra;
var i,u,v,min: longint;
begin
fillchar(dd,sizeof(dd),true);
for i:=1 to n do count[i]:=0;
count[1]:=1;
8



for 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.

9


ĐỀ 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 tố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;
Var

a,b:array[1..nMax] of longint;
f:text;
n,k,i:longint;
aMax,Low,High,Mid:int64;
Res:int64;
Sum:qword;

Procedure Enter;
Begin
10


Assign(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 LowBegin
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;
11


Main;
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]]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];
while (2*x<=num) do
begin
c := 2*x;
if (c+1<=num) and (T[H[c]]if (T[H[c]]H[x] := H[c];
x := c;
end;
H[x] := tmp;
END;

Procedure Get(Var x:longint);
12


BEGIN
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);
reset(fi);
rewrite(fo);
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
13


readln(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]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;

BEGIN
tmp := H[z][x];
while(2*x<=num[z]) do
begin
c := 2*x;
if(c+1<=num[z]) and (L[z][H[z][c]]>L[z][H[z][c+1]])
then inc(c);
if(L[z][H[z][c]] > L[z][tmp]) then break;
H[z][x] := H[z][c];
id[z][H[z][x]] := x;
14


x := 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;
15



BEGIN
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;
if (res = Ulti) then writeln(fo,-1)
else writeln(fo,res);
END;
BEGIN
assign(fi,FINP);
reset(fi);
Process;
close(fi);
close(fo);

assign(fo,FOUT);
rewrite(fo);

16


END.

17



×