You are on page 1of 35

MỤC LỤC

I. Khái niệm ................................................................................................................ 2


II. Các thao tác thường dùng đối với Heap Max.......................................................... 2
1. Khai báo ................................................................................................................... 2
2. UpHeap .................................................................................................................... 2
3. DownHeap ............................................................................................................... 3
4. Push ......................................................................................................................... 4
5. Pop ........................................................................................................................... 4
6. Dijkstra Heap ........................................................................................................... 5
III. Một số bài tập ứng dụng .......................................................................................... 6
1. Bài tập 1: Heapuri .................................................................................................... 6
2. Bài tập 2: Thả xốp ................................................................................................... 8
3. Bài tập 3: PILOT ................................................................................................... 10
4. Bài tập 4: Tiểu thuyết trinh thám ........................................................................... 13
5. Bài tập 5: Cezar ..................................................................................................... 18
6. Bài tập 6: Toy Cars ................................................................................................ 22
7. Bài tập 7: BASE3 .................................................................................................. 26
IV. Một số bài tập vận dụng ........................................................................................ 30
8. Bài tập 1: BOCSOI13 ............................................................................................ 30
9. Bài tập 2: Kế hoạch làm bài .................................................................................. 31
10. Bài tập 3: CATUN ................................................................................................. 31
11. Bài tập 3: Barbar .................................................................................................... 32
12. Bài tập 4: Cầu cảng ................................................................................................ 33
13. Bài tập 5: K TỔNG BÉ NHẤT ............................................................................. 33
14. Bài tập 6: BINLADEN .......................................................................................... 34
BINLADEN.INP ...................................................................................................................................... 34
BINLADEN.OUT ..................................................................................................................................... 34

V. Tài liệu tham khảo ................................................................................................. 35

1
CHUYÊN ĐỀ: CẤU TRÚC DỮ LIỆU HEAP

I. Khái niệm
Heap là một trong những cấu trúc dữ liệu đặc biệt quan trọng, nó giúp ta có thể giải
được nhiều bài toán trong thời gian cho phép. Độ phức tạp thông thường khi làm việc với
Heap là O(logN).
Heap thực chất là một cây cân bằng thỏa mãn các điều kiện sau:
• Một nút có không quá 2 nút con.
• Với Heap Max thì nút gốc là nút lớn nhất, mọi nút con đều không lớn hơn nút cha
của nó. Với Heap Min thì ngược lại.
Mặc dù được mô tả như cây nhưng Heap có thể được biểu diễn bằng mảng. Nút con
của nút i là 2*i và 2*i+1. Do Heap là cây cân bằng nên độ cao của 1 nút luôn <= logN.
Ứng dụng chủ yếu của Heap là tìm Min, Max trong 1 tập hợp động (có thể thay đổi,
thêm, bớt các phần tử) nhưng như vậy đã là quá đủ.

(Mô hình biểu diễn Heap bằng cây nhị phân và bằng mảng)
II. Các thao tác thường dùng đối với Heap Max
1. Khai báo
Ở ví dụ này, Heap sẽ là mảng một chiều kiểu LongInt, nHeap là số phần tử của mảng.
Const
maxn = 100000;
Var
nHeap : LongInt;
Heap : array[0..maxn] of LongInt;
2. UpHeap
Nếu 1 nút lớn hơn nút cha của nó thì di chuyển nó lên trên:

2
Procedure UpHeap(i : LongInt);
Begin
if (i = 1) or (Heap[i] < Heap[i div 2]) then exit; // Nếu i là nút gốc hoặc
nhỏ hơn nút cha thì không làm việc
swap(Heap[i] , Heap[i div 2]); // Đổi chỗ 2 phần tử trong Heap;
UpHeap(i div 2); // Tiếp tục di chuyển lên trên
end;
3. DownHeap
Nếu 1 nút nhỏ hơn nút con thì đẩy nó xuống dưới:

3
Procedure DownHeap(i : LongInt);
Var
j : LongInt;
Begin
j := i*2;
if j > nHeap then exit; // Nếu i không có nút con thì không làm việc
if (j < nHeap) and (Heap[j] < Heap[j+1]) then Inc(j); // Nếu i có 2 nút con thì
chọn nút ưu tiên hơn
if Heap[i] < Heap[j] then // Nếu nút cha nhỏ hơn nút con
begin
swap(Heap[i] , Heap[j]); // Đổi chỗ 2 phần tử trong Heap
DownHeap(j); // Tiếp tục di chuyển xuống dưới
end;
end;
4. Push
Đưa 1 phần tử mới vào Heap: Thêm 1 nút vào cuối Heap và tiến hành UpHeap từ
đây:
Procedure Push(x : LongInt);
Begin
Inc(nHeap); // Tăng số phần tử của Heap
Heap[nHeap] := x; // Thêm x vào Heap
UpHeap(nHeap);
End;
5. Pop
Rút ra 1 phần tử ở vị trí v trong Heap: Gán Heap[v] := Heap[nHeap] rồi tiến hành
chỉnh lại Heap:
Function Pop(v : LongInt) : LongInt;
Begin
Pop := Heap[v]; // Lấy phần tử ở vị trí v ra khỏi Heap
Heap[v] := Heap[nHeap]; // Đưa phần tử ở cuối Heap vào vị trí v
Dec(nHeap); // Giảm số phần tử của Heap đi 1

4
{Chỉnh lại Heap}
UpHeap(v);
DownHeap(v);
End;
Ngoài ra, khi sử dụng thuật toán Dijkstra/Prim kết hợp cấu trúc Heap, bạn còn
có thể sử dụng cách Push và Pop khác thuận lợi hơn so với cách trình bày ở trên:
6. Dijkstra Heap
1/ Update – Dijkstra:
Procedure Update(v : LongInt); // Đỉnh v vừa được sửa nhãn, cần chỉnh lại Heap
var
child , parent : LongInt;
begin
child := pos[v]; // child là vị trí của đỉnh v trong Heap
if child = 0 then // Nếu đỉnh v chưa có trong Heap
begin
Inc(nHeap); // Tăng số phần tử của Heap
child := nHeap; // Đưa v vào cuối Heap
end;
parent := child div 2; // parent là nút cha của child
while (parent > 0) and (d[Heap[parent]] > d[v]) do // Nếu đỉnh ở nút parent
kém ưu tiên hơn v thì bị “kéo xuống” nút child
begin
Heap[child] := Heap[parent]; // Đẩy đỉnh được lưu trong nút cha xuống nút con
pos[Heap[child]] := child; // Ghi nhận lại vị trí mới của đỉnh đó
child := parent; // Tiếp tục di chuyển lên
parent := child div 2;
end;
Heap[child] := v; // Thao tác “kéo xuống” ở trên sẽ tạo ra 1 ô trống ở nút child để đặt v vào
pos[v] := child; // Ghi nhận vị trí mới của đỉnh v trong Heap
end;
2/ Pop – Dijkstra:
Function Pop : LongInt; // Lấy từ Heap đỉnh có nhãn tự do nhỏ nhất
var
r , v , c : LongInt;
begin
Pop := Heap[1]; // Nút gốc là nút có nhãn tự do nhỏ nhất
v := Heap[nHeap]; // v là đỉnh ở nút lá cuối Heap, sẽ được đảo lên đầu và vun đống
Dec(nHeap); // Giảm số phần tử của Heap
r := 1; // Bắt đầu từ nút gốc
while r*2 <= nHeap do // Chừng nào r chưa phải là lá
begin
c := r*2; // c là nút con của r
if (c <nHeap) and (d[Heap[c]] > d[Heap[c+1]]) then Inc(c); // Trong
2 nút con chọn nút con chứa đỉnh ưu tiên hơn
if d[Heap[c]] >= d[v] then break; // Nếu v ưu tiên hơn thì không làm việc nữa
Heap[r] := Heap[c]; // Chuyển đỉnh lưu ở nút con lên nút cha
pos[Heap[r]] := r; // Cập nhật lại vị trí mới trong Heap của đỉnh đó
r := c; // Tiếp tục di chuyển xuống dưới
end;

5
Heap[r] := v; // Đỉnh v được đặt vào vị trí r để đảm bảo cấu trúc Heap
pos[v] := r; // Ghi nhận vị trí mới của đỉnh v trong Heap
end;
III. Một số bài tập ứng dụng
1. Bài tập 1: Heapuri
Cho danh sách gồm N phần tử. Chúng ta thực hiện một số thao tác trên danh sách. Có 3
loại thao tác sau :
- Thao tác 1 : Cho phần tử x vào danh sách
- Thao tác 2 : Xóa phần tử thứ t (theo thứ tự nhập vào)
- Thao tác 3: Lấy ra phần tử nhỏ nhất trong danh sách
Yêu cầu: Hãy cho biết các kết quả của thao tác 3 ?
INPUT: HEAPURI.INP
- Dòng 1: N
- Các dòng tiếp theo là dãy thao tác cần xử lý theo định dạng 1 x, 2 x hoặc 3 tương
ứng là thao tác 1, 2 và 3
OUTPUT : HEAPURI.OUT
- Ghi trên nhiều dòng, mỗi dòng là kết quả của thao tác 3 theo thứ tự trong file input.
Ví dụ :
HEAPURI.INP HEAPURI.OUT

9 4
1 4 2
1 7 7
1 9
3
1 2
2 1
3
2 4
3

Dễ thấy đây là một danh sách động (số lượng phần tử thay đổi), vì vậy ta xây dựng một
heapmin để lấy ra giá trị nhỏ nhất ở các thao tác 3. Để xóa phần tử thứ t theo thứ tự nhập
vào thì ta lưu thêm một mảng Pos.
#include <stdio.h>
#include <assert.h>

#define maxn 200010

int N, L, NR;
int A[maxn], Heap[maxn], Pos[maxn];

void push(int x)

6
{
int aux;

while (x/2 && A[Heap[x]]<A[Heap[x/2]])


{
aux = Heap[x];
Heap[x] = Heap[x/2];
Heap[x/2] = aux;

Pos[Heap[x]] = x;
Pos[Heap[x/2]] = x/2;
x /= 2;
}
}

void pop(int x)
{
int aux, y = 0;

while (x != y)
{
y = x;

if (y*2<=L && A[Heap[x]]>A[Heap[y*2]]) x = y*2;


if (y*2+1<=L && A[Heap[x]]>A[Heap[y*2+1]]) x = y*2+1;

aux = Heap[x];
Heap[x] = Heap[y];
Heap[y] = aux;

Pos[Heap[x]] = x;
Pos[Heap[y]] = y;
}
}

intmain()
{
freopen("heapuri.in", "r", stdin);
freopen("heapuri.out", "w", stdout);

int i, x, cod;

scanf("%d ", &N);

assert(1<=N && N<=200000);

for (i=1; i<=N; i++)


{
scanf("%d ", &cod);
assert(1<=cod && cod<=3);
if (cod < 3)

7
{
scanf("%d ", &x);
assert(1<=x && x<=1000000000);
}

if (cod == 1)
{
L++, NR++;
A[NR] = x;
Heap[L] = NR;
Pos[NR] = L;

push(L);
}

if (cod == 2)
{
A[x] = -1;
assert(Pos[x] != 0);
assert(1<=x && x<=NR);
push(Pos[x]);

Pos[Heap[1]] = 0;
Heap[1] = Heap[L--];
Pos[Heap[1]] = 1;
if (L>1) pop(1);
}

if (cod == 3) printf("%d\n", A[Heap[1]]);


}

return 0;
}

2. Bài tập 2: Thả xốp


Có N hạt xốp, hạt thứ i có khối lượng , được thả lần lượt xuống một ống nước đặc
biệt được thiết kế sao cho tại mỗi thời điểm chỉ có một hạt xốp nhẹ nhất nổi lên trên bề
mặt. Trước mỗi lần thả, hạt xốp đang nổi trên bề mặt sẽ bị ngấm nước và tăng gấp đôi
khối lượng. Hỏi sau khi thả hạt xốp cuối cùng vào ống thì khối lượng xốp tăng so với tổng
khối lượng ban đầu là bao nhiêu ?
INPUT:SPONGE.INP
- Dòng 1: Số nguyên dương N
- Dòng 2: N số nguyên dương
OUTPUT :SPONGE.OUT
- Ghi 1 số duy nhất là đáp án của bài toán
Ví dụ:

8
SPONGE.INP SPONGE.OUT
3 3
2 1 3
Tương tự bài 1, dễ thấy đây là một danh sách động (giá trị phần tử thay đổi), vì vậy ta xây
dựng một heapmin để lấy ra hạt xốp nhỏ nhất, tăng gấp đôi khối lượng rồi cập nhật lại
heapmin.
COnst
tfi='sponge.in';
tfo='sponge.out';
Type
arr1=array[0..100001] of longint;
Var
fi,fo : text;
w,heap,head,ke,d,c,dd,cost : arr1;
n,nheap,delta : longint;

{==================================}
Procedure nhap;
Var
i :longint;
Begin
Assign(fi,tfi);Reset(fi);
read(fi,n);
For i := 1 to n do
read(fi,w[i]);
cost := w;
Close(fi);
End;
{==================================}
Procedure doicho(varx,y : longint);
var
tmp :longint;
Begin
tmp := x;
x := y;
y := tmp;
End;
{==================================}
Procedure upheap(i : longint);
Var
j :longint;
Begin
While (i <> 1) and (heap[i] <heap[i div 2]) do
Begin
doicho(heap[i],heap[i div 2]);
i := i div 2;
End;
End;
{=============================}

9
Procedure downheap(i : longint);
Var
j :longint;
Begin
While (2*i <= nheap) do
Begin
j := 2*i;
If (heap[j] >heap[j+1]) and (j <nheap) then inc(j);
If heap[i] > heap[j] then
Begin
doicho(heap[i],heap[j]);
i :=j;
End
else exit;
End;
End;
{=============================}
Procedure push(x :longint);
Begin
inc(nheap);
heap[nheap] := x;
upheap(nheap);
End;
{=============================}
Procedure xuly;
Var
i1 :longint;
Begin
For i1 := 1 to n-1 do
Begin
push(w[i1]);
delta := delta + heap[1];
heap[1] := heap[1]*2;
downheap(1);
End;
End;
{==================================}
BEGIN
Assign(fo,tfo);Rewrite(fo);
nhap;
xuly;
write(fo,delta);
Close(fo);
END.
3. Bài tập 3:PILOT
HT AIRLINE là một hãng hàng không danh tiếng ở Việt Nam, tuy nhiên, để tồn tại
trong cơn bão suy thoái kinh tế, Ban giám đốc quyết định giảm chi phi tiền lương cho phi
công càng nhiều càng tốt.

10
HT airline có tất cả N phi công (N là số chẵn), các phi công được đánh số từ 1 đến N
(Phi công 1 là phi công trẻ nhất, phi công i là phi công có tuổi cao thứ i,… phi công n là
N
phi công cao tuổi nhất). HT airline cần chính xác phi hành đoàn, mỗi phi hành đoàn
2
gồm 2 phi công (một lái chính và một lái phụ), lái chính phải nhiều tuổi hơn lái phụ.
Hợp đồng mà công ty kí với các phi công có 2 điều khoản rõ ràng: tiền lương khi là lái
chính và tiền lương khi là lái phụ. Rõ ràng, đối với 1 phi công, tiền lương lái chính bao giờ
cũng cao hơn tiền lương khi lái phụ. Tuy nhiên, với một phi hành đoàn, có thể tiền lương
của lái chính lại thấp hơn lái phụ.
N
Để giảm chi phí trả tiền lương, HT phải xác định một cách phân chia tối ưu phi
2
hành đoàn.
Bạn hãy giúp HT viết chương trình xác định số tiền tối thiểu để trả lương cho N phi
công.
INPUT: PILOT.INP
- Dòng 1 : Số nguyên dương N, là số phi công ở HT airline.(2  N  10000; N là số
chẵn)
- N dòng tiếp theo, dòng thứ i là thông tin về phi công i : gồm hai số a và c viết cách
nhau 1 dấu cách trống, tương ứng là tiền lương khi lái chính và tiền lương khi lái
phụ (1  a < c  100.000).
OUTPUT: PILOT.OUT
- Một số nguyên duy nhất là tiền lương tối thiểu phải trả cho N phi công.
Ví dụ :
PILOT.INP PILOT.OUT PILOT.INP PILOT.OUT

6 32000 6 33000
10000 7000 5000 3000
9000 3000 4000 1000
6000 4000 9000 7000
5000 1000 11000 5000
9000 3000 7000 3000
8000 6000 8000 6000
Time limits / test: 1s
Ta có nhận xét sau: Theo thứ tự i nhập vào từ 1 đến N, thì cứ i lẻ thì phải có 1 lái phụ. Vì
vậy, ta xây dựng một heap max, lần lượt cho vào heap, khi i lẻ thì ta lấy trong
heap ra một nút, đây chính là lái phụ.
CONST
tfi = 'pilot.inp';
tfo = 'pilot.out';
max = 100000;
TYPE

11
Arr1 = array[1..max] of longint;
VAR
fi,fo : text;
n,nh,sum,kq : longint;
a,b,h : Arr1;
{*-----------------------------------------------------------------*}
procedurenhap;
var
i : longint;
begin
assign(fi,tfi);reset(fi);
read(fi,n);
For i := 1 to n do
read(fi,a[i],b[i]);
close(fi);
end;
{*-----------------------------------------------------------------*}
proceduredoicho(varx,y : longint);
var
tg : longint;
begin
tg := x;
x := y;
y := tg;
end;
{*-----------------------------------------------------------------*}
procedureupheap(i : longint);
begin
if (i = 1) or (h[i] < h[i div 2]) then exit;
if h[i div 2] < h[i] then
begin
doicho(h[i div 2],h[i]);
upheap(i div 2);
end;
end;
{*-----------------------------------------------------------------*}
proceduredownheap(i : longint);
var
j : longint;
begin
j := 2 *i;
if j >nh then exit;
if (j <nh) and (h[j] < h[j+1]) then inc(j);
if h[i] < h[j] then
begin
doicho(h[i],h[j]);
downheap(j);
end;
end;
{*-----------------------------------------------------------------*}
procedure push(x : longint);

12
begin
inc(nh);
h[nh] := x;
upheap(nh);
end;
{*-----------------------------------------------------------------*}
function pop : longint;
begin
pop := h[1];
h[1] := h[nh];
dec(nh);
downheap(1);
end;
{*-----------------------------------------------------------------*}
procedurexuli;
var
i : longint;
begin
sum := 0;nh := 0;kq := 0;

For i := 1 to n do
begin
sum := sum + a[i];
push(a[i] - b[i]);
if i mod 2 = 1 then kq := kq + pop;
end;
end;
{*-----------------------------------------------------------------*}
procedureinkq;

begin
assign(fo,tfo);rewrite(fo);
write(fo,sum-kq);
close(fo);
end;
{*-----------------------------------------------------------------*}
{*-----------------------------------------------------------------*}
{*-----------------------------------------------------------------*}
BEGIN
nhap;
xuli;
inkq;
END.
4. Bài tập4:Tiểu thuyết trinh thám
Ivan Đneprôp viết truyện tiểu thuyết trinh thám. Truyện của anh ta không có gì đặc
sắc: không có các tình huống ly kỳ đặc biệt, cũng không có các chi tiết hài hước tế nhị.
Thậm chí một hiệu sách đã bán các sáng tác của Ivan theo cân! Nhưng độc giả lại thích
truyện của Ivan. Nó dễ hiểu và giúp người ta thư giản sau một ngày lao động mệt nhọc.

13
Thực ra, bí mật sự thành công của Ivan là ở chổ không phải chính anh nghĩ ra các tình
huống mà là người em trai Alexei. Ivan có nhiệm vụ viết nó thành các bestsellers. Dĩ
nhiên hai anh em chia nhau hợp lý số tiền kiếm được. Điều đáng tiếc là khả năng sáng tạo
các tình huống ly kỳ của Alexei lại phụ thuộc vào chu kỳ sinh học của anh. Hai anh em
phân tích bảng chu kỳ sinh học của Alexei và thấy rằng trong thời gian tới Alexei sẽ nghĩ
được n chủ đề mới, chủ đề thứ i sẽ được nghĩ ra ở ngày ri. Trong cùng một ngày có thể
Alexei nghĩ ra tới vài câu chuyện.
Với mỗi chủ đề, Ivan thời lượng cần thiết để hoàn thành tác phẩm và tính được rằng
chủ đề thứ i cần có pi ngày để viết. Ivan có trí nhớ cực tốt, vì vậy anh có thể tạm bỏ dở
một truyện, chuyển sang viết truyện khác sau đó quay lại hoàn thành nốt các truyện dở
dang.
Dĩ nhiên, hai anh em muốn sách được viết càng nhanh càng tốt, tức là phải cực tiểu
hóa thời gian trung bình từ thời điểm hiện tại tới thời điểm lúc tiểu thuyết hoàn thành. Vì
số sách là cố định, nên điều này tương đương với việc cực tiểu hóa tổng thời gian viết tất
cả các cuốn sách. Điều này có nghĩa là nếu cuốn sách thứ i được hoàn thành vào ngày ci
thì c i phải được cực tiểu hóa. Ví dụ, ở ngày thứ nhất Alexei nghĩ ra một cốt chuyện mà
Ivan cần viết trong 5 ngày, Ivan bắt tay viết ngay. Ngày thứ 2 Alexei nghĩ thêm một cót
chuyện mới cần viết trong một ngày. Ivan chuyển sang viết chuyện mới, ngày thứ 3: chuện
thứ hai hoàn thành và Ivan quay lại viết tiếp chuyện thứ nhất, mất thêm 4 ngày nữa, đến
ngày thứ 7 chuyện thứ nhất hoàn thành. Tổng các thời điểm hoàn thành là 3+7 = 10.
Yêucầu: Cho n, ri và pi. Hãy xác định tổng thời gian ngắn nhất hoàn thành tất cả các
truyện.
INPUT:PULP.INP:
- Dòng 1: Chứa số nguyên n (1 ≤ n ≤ 100.000)
- Mỗi dòng trong n dòng sau chứa 2 số nguyên ri và pi.
OUTPUT: PULP.OUT
- Một số nguyên – tổng thời gian ngắn nhất tìm được.
Ví dụ:
PULP.INP PULP.OUT

2 10
15
21
Thuật toán:
1. Sort tăng theo R[i]
2. Ví dụ với các thời điểm , dễ dàng ta thấy
3. Với mỗi khoảng thời gian t = R[i] – R[i-1], ta ưu tiên giải quyết công việc cần ít
thời gian nhất, giả sử là công việc P (Thấy ngay là sử dụng Heap để lấy công việc
có thời gian nhỏ nhất)
a. Nếu thời gian thực hiện công việc P < t thì thực hiện hết công việc P, thời
gian còn lại thực hiện tiếp các công việc có thời gian nhỏ tiếp theo.
14
b. Nếu thời gian thực hiện công việc P > t thì chúng ta thực hiện công việc P
trong khoảng thời gian t, thời gian còn lại là t[P] – t ta lưu lại trong Heap để
tính tiếp.
4. Sau khi xét đến thời điểm N, lúc này ta sẽ phải làm tất cả các công việc còn lại tuần
tự từ nhỏ đến lớn, hay là lấy tất cả các phần tử trong Heap ra là xong.

CONST
tfi = 'pulp.inp';
tfo = 'pulp.out';
max = 100000;
TYPE
Arr1 = array[1..max] of longint;
VAR
fi,fo : text;
n,nh,sum,res : longint;
r,p,h : Arr1;
{*------------------------------------------------------------*}
procedurenhap;
var
i : longint;
begin
assign(fi,tfi);reset(fi);
read(fi,n);
For i := 1 to n do
read(fi,r[i],p[i]);
close(fi);
end;
{*------------------------------------------------------------*}
proceduredoicho(varx,y : longint);
var
tg : longint;
begin
tg := x;
x := y;
y := tg;
end;
{*------------------------------------------------------------*}
procedureupheap(i : longint);
begin
if (i = 1) or (h[i] > h[i div 2]) then exit;
if h[i div 2] > h[i] then
begin
doicho(h[i div 2],h[i]);
upheap(i div 2);
end;
end;
{*------------------------------------------------------------*}
proceduredownheap(i : longint);
var

15
j : longint;
begin
j := 2 * i;
if j >nh then exit;
if (j <nh) and (h[j] > h[j+1]) then inc(j);
if h[i] > h[j] then
begin
doicho(h[i],h[j]);
downheap(j);
end;
end;
{*------------------------------------------------------------*}
procedure push(x : longint);
begin
inc(nh);
h[nh] := x;
upheap(nh);
end;
{*------------------------------------------------------------*}
function pop : longint;
begin
pop := h[1];
h[1] := h[nh];
dec(nh);
downheap(1);
end;
{*------------------------------------------------------------*}
procedurexuli;
var
i,t,x : longint;
begin
nh := 0;
push(p[1]);
sum := r[1];
res := 0;
For i := 2 to n do
begin
t := r[i] - r[i-1];
while (nh> 0) and (t > 0) do
begin
x := pop;
if (x <= t) then
begin
t := t - x;
sum := sum + x;
res := res + sum;
end
else
begin
push(x-t);
t := 0;

16
end;
end;
push(p[i]);sum := r[i];
end;
whilenh> 0 do
begin
x := pop;
sum := sum + x;
res := res + sum;

end;
end;
{*------------------------------------------------------------*}
procedure sort(x,y:longint);
var
i,j,key1,key2 : longint;
begin
i := x;
j := y;
key1 := r[x+random(y-x+1)];
key2 := p[x+random(y-x+1)];
repeat
while (r[i] < key1) or ((r[i] = key1) and (p[i] < key2)) do inc(i);
while (r[j] > key1) or ((r[j] = key1) and (p[j] > key2)) dodec(j);
if i <= j then
begin
doicho(r[i],r[j]);
doicho(p[i],p[j]);
inc(i);
dec(j);
end;
until i > j ;
if x < j then sort(x,j);
if y > i then sort(i,y);
end;

{*------------------------------------------------------------*}
{*------------------------------------------------------------*}
procedureinkq;
begin
assign(fo,tfo);rewrite(fo);
write(fo,res);
close(fo);
end;

{*------------------------------------------------------------*}
{*------------------------------------------------------------*}
{*------------------------------------------------------------*}
{*------------------------------------------------------------*}
BEGIN
randomize;

17
nhap;
sort(1,n);
xuli;
inkq;
END.

5. Bài tập 5: Cezar


Tại HT Land, có tất cả n thượng nghị sĩ ở trong n ngôi biệt thự (đánh số từ 1 đến n),
giữa 2 ngôi nhà có một đường đi duy nhất: đường đi trực tiếp hoặc không trực tiếp (đi qua
các con đường khác). Tất cả các ngôi nhà đều đi được đến nhau.
Mỗi thượng nghị sĩ khi đi từ nhà mình đến thượng viện, phải trả 1 USD khi đi qua
một con phố (phố = đường nối trực tiếp 2 nhà bất kỳ). HT – người đứng đầu thượng viện -
đã nghĩ cách làm sao cho số tiền mà các thượng nghĩ sĩ phải trả là tối thiểu. Vì vậy, HT
quyết định
• Có k con phố miễn phí (thượng nghị sĩ sẽ không phải trả tiền khi đi trên con
phố này)
• Đặt tòa nhà thượng viện ở một trong n ngôi nhà
Bạn hãy viết chương trình tính xem chi phí tối thiểu là bao nhiêu?
INPUT: CEZAR.INP
- Dòng 1: Số nguyên n và k tương ứng là số ngôi nhà ở HT land và số đường phố
miễn phí
- N – 1 dòng tiếp theo, mỗi dòng chứa 2 số i j có nghĩa là có con phố nối ngôi nhà i
và ngôi nhà j
OUTPUT: CEZAR.OUT
Chi phí tối thiểu phải trả
Giới hạn:

Ví dụ
6
1 3
CEZAR.INP CEZAR.OUT Giải thích
4 5
13 3 11
12 Có nhiều phương án 2 8 7
23 giải quyết: Đây là 1
28 phương án: 10 13
9
78
– 5-7, 7-8, 8-10
75
54 là những 11 12

56 đường miễn
89 phí
8 10 – 8 là thượng
10 11 viện
10 12 Chi phí tối thiểu là:
10 13

18
11.

Time limit:0.5 s/test


Thuật toán:
1. Chúng ta sẽ tìm con đường phải trả phí đi lại.
2. Khởi tạo D[i] = trọng số của đỉnh i với ý nghĩa = số lượng người đi đến thượng
viện phải đi qua con đường này. Ban đầu
3. Tại mỗi bước chúng ta sẽ cho các nút lá vào Heap, mỗi lần lấy 1 phần tử trong heap
chúng ta sẽ update lại trọng số của đỉnh kề với đỉnh vừa lấy ra và nếu đỉnh vừa
update thành nút lá ta lại cho vào trong heap. Sau khi lấy xong đỉnh thì cập
nhật đáp án.
Const
tfi='cezar.inp';
tfo='cezar.out';
oo = 1000000000;
Type
arr1=array[0..100000] of longint;
arr2=array[0..100000] of boolean;
arr3=array[0..15000] of longint;
arr4=array[0..15000] of arr3;
Var
fi,fo : text;
heap,head,ke,d,c,pos,cost,trace,deg : arr1;
free : arr2;
k,n,nheap,f,r,sum,res : longint;
count : arr4;

{===============================}
Procedure nhap;
Var
i,u,v : longint;
Begin
Assign(fi,tfi);Reset(fi);
read(fi,n,k);
For i := 1 to n-1 do
Begin
read(fi,u,v);
d[i] := u;
c[i] := v;
inc(deg[u]);
inc(deg[v]);
ENd;
head[1] := deg[1];
For i := 2 to n+1 do
Begin
head[i] := head[i-1] + deg[i];

19
End;
For i := 1 to n-1 do
Begin
u := d[i];
v := c[i];
ke[head[u]] := v;
ke[head[v]] := u;
cost[head[u]] := 1;
cost[head[v]] := 1;
dec(head[u]);
dec(head[v]);
End;
Close(fi);
End;
{===============================}
Procedure khoitao;
Var
i :longint;
Begin
Fillchar(free,sizeof(free),true);
For i := 1 to n do
BEgin
pos[i] := 0;
cost[i] := 1;
End;
nheap := 0;
End;
{===============================}
Procedure downheap(root : longint);
Var
child,u : longint;
BEgin
u := heap[root];
repeat
child := root*2;
If (child <nheap) and (cost[heap[child]] > cost[heap[child+1]])
then inc(child);
If (child >nheap) or (cost[heap[child]]>= cost[u]) then break;
heap[root] := heap[child];
pos[heap[root]] := root;
root := child;
Until false;
heap[root] := u;
pos[u] := root;
End;
{===============================}
Procedure upheap(child : longint);
Var
root,u : longint;
Begin
u := heap[child];

20
Repeat
root := child div 2;
If (root=0) or (cost[heap[root]] <= cost[u]) then break;
heap[child] := heap[root];
pos[heap[child]] := child;
child := root;
Until false;
heap[child] := u;
pos[u] := child;
End;
{===============================}
FUnctionpop :longint;
Begin
pop := heap[1];
heap[1] := heap[nheap];
pos[heap[1]] := 1;
dec(nheap);
downheap(1);
End;
{===============================}
Procedure push(x :longint);
Begin
inc(nheap);
heap[nheap] := x;
pos[heap[nheap]] := nheap;
upheap(nheap);
End;
{===============================}
Procedure update(v : longint);
Var
i :longint;
Begin
For i := head[v] + 1 to head[v+1] do
Begin
dec(deg[ke[i]]);
cost[ke[i]] := cost[ke[i]] + cost[v];
If deg[ke[i]] = 1 then push(ke[i]);
End;
End;
{===============================}
Function dijkstra(x :longint) : longint ;
Var
i,u : longint;
Begin
push(x);
repeat
u := pop;
free[u] := false;
update(u);
untilnheap = 0;
End;

21
{===============================}
Procedure xuly;
Var
i,u : longint;
Begin
khoitao;
res := 0;
For i := 1 to n do
Begin
If deg[i]=1 then push(i);
End;
repeat
u := pop;
sum := sum + cost[u];
inc(res);
If res=n-1-k then break;
free[u] := false;
update(u);
until false;

End;
{===============================}
Procedure inkq;
Begin
Assign(fo,tfo);REwrite(fo);
write(fo,sum);
Close(fo);
End;
{===============================}
BEGIN
nhap;
xuly;
inkq;
END.

6. Bài tập 6:Toy Cars


Bé Tom là một đứa trẻ ba tuổi và rất thích chơi đồ chơi oto. Bé Tom có n chiếc oto
khác nhau, chúng được đặt trên một chiếu giá cao mà Tom không thể tự mình lấy được.
Phòng của Tom cũng rất nhỏ, tại một thời điểm, không thể có nhiều hơn k chiếc oto đồ
chơi ở trên sàn nhà.
Tom chơi với một trong nhưng chiếc oto trên sàn nhà, Mẹ của Ton luôn ở trong
phòng với Tom trong cả thời gian chơi của Tom. Khi Bé Tom muốn chơi với một chiếc
oto khác, nếu chiếc này ở trên sàn nhà, Tom sẽ tự lấy để chơi, còn nếu chiếc oto này ở trên
giá, Mẹ của Tom sẽ lấy xuống cho Tom. (Khi Mẹ của Tom lấy 1 chiếc oto cho Tom,cùng
lúc cô ấy có thể lấy một chiếc oto bất kỳ khác ở sàn nhà để đặt lên giá – để có đủ khoảng
không gian cho k chiếc oto)

22
Mẹ của Tom là người mẹ rất hiểu ý thích của con mình, cô ta có thể biết được
những chiếc oto nào mà con trai mình muốn chơi. Cô ta muốn biết số lần ít nhất mà cô ta
giúp Tom lấy xe oto từ trên giá.
INPUT: TOYCARS.INP
- Dòng 1: 3 số nguyên dương N, K, P
lần lượt là số lượng oto mà Tom có, số lượng oto có thể đặt trên sàn tại cùng một
thời điểm và độ dài dãy các oto mà Tom muốn chơi. Các oto được đánh số từ 1 đến
N.
- P dòng tiếp theo, mỗi dòng 1 số nguyên dương là chiếc oto mà Tom muốn chơi
(theo thứ tự thời gian)
OUTPUT: TOYCARS.OUT
- Một số nguyên duy nhất là số lần ít nhất Mẹ Tom lấy oto từ trên giá xuống.
Ví dụ:
TOYCARS.INP TOYCARS.OUT
327 4
1
2
3
1
3
1
2

Thuật toán :
1. Gọi Next[i] = thời điểm gần nhất sau thời điểm i mà bé Tom muốn chơi món đồ
chơi P[i]. Next[i] = nếu i là thời điểm cuối cùng bé Tom muốn chơi món P[i].
Như vậy có thể coi Next[i] = độ tồi của việc giữ lại món đồ chơi P[i] ở trên sàn
(Next[i] càng lớn tức là việc giữ lại P[i] càng vô ích).
2. Xét lần lượt các thời điểm i :
a. Nếu món P[i] đang nằm trên sàn : bỏ qua và xét thời điểm tiếp theo.
b. Nếu mon P[i] đang nằm trên giá :
i. Nếu sàn chưa đầy : Ta lấy món P[i] để lên sàn.
ii. Nếu sàn đầy : Ta chọn từ sàn một món đồ chơi có độ tồi lớn nhất và
đặt lên giá, sau đó lấy món P[i] để lên sàn.
3. Dễ dàng nhận thấy có thể sử dụng cấu trúc Heap để thực hiện thuật giải trong thời
gian N.logK.
const
tfi = 'toycars.inp';
tfo = 'toycars.out';
type
arr1 = array[0..1000000] of longint;
arr2 = array[0..100000] of boolean;

23
var
fi,fo:text;
n,k,p,res,nheap,x,dem,vc:longint;
c,deg,d,a,pos,b,vt,next:arr1;
free:arr2;
{------------------------------------------------------------------------}
procedurenhap;
vari,j:longint;
begin
assign(fi,tfi);reset(fi);
read(fi,n,k,p);
for i:=1 to p do
read(fi,c[i]);
close(fi);
end;
{------------------------------------------------------------------------}
procedurekhoitao;
vari,j:longint;
begin
res:=0;
for i:=1 to n do
begin
free[i]:=true;
vt[i]:=p+1;
end;
for i:=p downto 1 do
begin
b[i]:=vt[c[i]];
vt[c[i]]:=i;
end;
end;
{------------------------------------------------------------------------}
proceduredoicho(vari,j:longint);
vartg:longint;
begin
tg:=i;
i:=j;
j:=tg;
end;
{-----------------------------------------------------------------------}
procedureupheap(i:longint);
varj,k:longint;
begin
if (i=1)or(d[a[i]]<=d[a[i div 2]]) then exit;
j:=i div 2;
doicho(a[i],a[j]);
doicho(pos[a[i]],pos[a[j]]);
upheap(j);
end;
{-----------------------------------------------------------------------}
proceduredownheap(i:longint);

24
var j:longint;
begin
j:=i*2;
if j>nheap then exit;
if (d[a[j]]<d[a[j+1]])and(j<nheap) then inc(j);
if d[a[j]]>d[a[i]] then
begin
doicho(a[i],a[j]);
doicho(pos[a[i]],pos[a[j]]);
downheap(j);
end;
end;
{-----------------------------------------------------------------------}
procedure push(i,j:longint);
begin
inc(nheap);
a[nheap]:=i;
pos[i]:=nheap;
d[i]:=b[j];
upheap(nheap);
end;
{-----------------------------------------------------------------------}
procedure pop;
vari,j:longint;
begin
x:=a[1];
a[1]:=a[nheap];
pos[a[nheap]]:=1;
dec(nheap);
downheap(1);
end;
{-----------------------------------------------------------------------}
procedurexuly;
vari,j:longint;
begin
for i:=1 to p do
if free[c[i]] then
begin
ifnheap=k then
begin
pop;
free[x]:=true;
end;
push(c[i],i);
free[c[i]]:=false;
inc(res);
end
else
begin
d[c[i]]:=b[i];
upheap(pos[c[i]]);

25
end;
end;
{------------------------------------------------------------------------}
procedureinkq;
vari,j:longint;
begin
assign(fo,tfo);rewrite(fo);
write(fo,res);
close(fo);
end;
{------------------------------------------------------------------------}
BEGIN
nhap;
khoitao;
xuly;
inkq;
END.

7. Bài tập 7:BASE3


Cho 3 số ở hệ cơ số 3 (viết bởi 3 số 0, 1, 2). Hãy tìm một chữ số N ở hệ cơ số 3 sao cho N
có một số lẻ chữ số và số 1 nằm ở chính giữa số N. Số N phải thu được từ việc liên kết 3
số cho trước, mỗi số trong 3 số có thể được sử dụng 0 hoặc nhiều lần:
Yêu cầu:
Xác định số lượng nhỏ nhất các chữ số của N thỏa mãn tính chất đầu bài
INPUT: BASE3.INP
- Gồm 3 dòng, mỗi dòng là một số ở hệ cơ số 3.
OUTPUT: BASE3.OUT
- Số lượng chữ số nhỏ nhất của N. Nếu không tồn tại N thì ghi số 0.
Giới hạn
- Số chữ số của một số trong 3 số đầu bài cho trong khoảng từ 1 đến 16.000.
Ví dụ:
BASE3.IN BASE3.OUT
001 13
020
2020
Giải thích
N = 2020001001001 .
Time limit/test: 0.4
L 1 R
Ký tự 1 ở giữa của xâu kết quả phải thuộc 1 trong 3 xâu:
Giả sử ký tự 1 của kết quả nằm ở vị trí I của xâu , ta sẽ xây dựng xâu kết quả
bằng cách thêm từng xâu vào trái hoặc phải (phần L là trái, ký tự 1 ở giữa, phần R là phải)
Ta sẽ xây dựng xâu đến khi thì dừng lại (yêu cầu đề bài)

26
Dễ thấy, ta chỉ quan tâm tới ghi nhận kết quả. Tức là nếu
tồn tại cách xây dựng L’, R’ mà

thỏa mãn cũng sử dụng cách xây dựng đó được với các cặp với
, chỉ cần lưu 1 cặp có
Quy hoạch động = cách xếp có với
Nếu mỗi lần ta luôn nắp xâu vào nửa ngắn hơn thì luôn đảm bảo
(do ). Với trạng thái , ta cập nhật cho trạng
thái + độ dài xâu nắp thêm vào. Bài toán trở thành Dijkstra Heap
về trạng thái . Các trạng thái cơ sở có thể là các ký tự 1 của 1 trong 3 xâu ban đầu.
Đáp án bài toán là
{$H+}
uses math ;
const
tfi = 'Base3.inp';
tfo = 'Base3.out';
Nmax = 16001;
vc = 1000000000000000000 ;
type
arr1 = array[0..Nmax] of longint ;
var
fi,fo : text;
n,m,nh,ss,xi,yi : longint;
s : array[1..3] of string ;
len : array[1..3] of longint ;
d : array[-Nmax..Nmax,0..1] of int64 ;
pos : array[-Nmax..Nmax,0..1] of longint;
h,h2 : array[0..4*Nmax] of longint;
procedureNhap;
var
i,j :longint;
begin
ss := 0;
for i := 1 to 3 do
begin
readln(fi,s[i]);
len[i] := length(s[i]);
ss := max(ss,len[i]) ;
end;
end;
procedureUpheap(i:longint) ;
var
x,y:longint ;
begin
x := h[i] ;

27
y := h2[i];
while (i > 1) and (d[x,y] < d[h[i div 2],h2[i div 2]]) do
begin
h[i] := h[i div 2] ;
h2[i] := h2[i div 2];
pos[h[i],h2[i]] := i ;
i := i div 2;
end ;
h[i] := x;
h2[i] := y;
pos[x,y] := i;
end;
proceduredownheap(i:longint);
var
j,x,y:longint;
begin
x := h[i];
y := h2[i];
while i * 2 <= nh do
begin
j := i * 2 ;
if (j <nh) and (d[h[j],h2[j]] >d[h[j+1],h2[j+1]]) then
inc(j);
if d[x,y] <= d[h[j],h2[j]] then break ;
h[i] := h[j];
h2[i] := h2[j];
pos[h[i],h2[i]] := i;
i := j;
end ;
h[i] := x;
h2[i] := y;
pos[x,y] := i ;
end;
procedure Push(i,j:longint);
begin
ifpos[i,j] = 0 then
begin
inc(nh) ;
h[nh] := i;
h2[nh] := j;
pos[i,j] := nh;
Upheap(nh) ;
end else Upheap(pos[i,j]) ;
end ;
procedure Pop;
begin
xi := h[1];
yi := h2[1];
h[1] := h[nh];
h2[1] := h2[nh];
pos[h[1],h2[1]] := 1;

28
dec(nh);
downheap(1) ;
end;
procedure Update ;
var
i,j,k:longint;
begin
for i := 1 to 3 do
begin
if s[i][1] = '0' then k := 0 else k := 1;
j := xi + len[i];
if (j <= ss) and (d[j,k] > d[xi,yi] + len[i]) then
begin
d[j,k] := d[xi,yi] + len[i] ;
push(j,k) ;
end;
j := xi - len[i] ;
if (j >= -ss) and (d[j,yi] > d[xi,yi] + len[i]) then
begin
d[j,yi] := d[xi,yi] + len[i];
push(j,yi) ;
end ;
end;
end ;
procedureinit;
var
i,j,k,u: longint;
begin
for i := -ss to ss do
for j := 0 to 1 do
d[i,j] := vc ;
for i := 1 to 3 do
for j := 1 to len[i] do if s[i][j] = '1' then
begin
if s[i][1] = '0' then k := 0 else k := 1;
u := (j - 1) - (len[i] - j) ;
d[u,k] := len[i] ;
push(u,k) ;
end;
end;
procedurexuly;
var
i,j,k: longint;
begin
whilenh> 0 do
begin
pop;
if (xi = 0) then
begin
write(fo,d[xi,yi]);
exit;

29
end;
update;
end;
write(fo,0) ;
end ;
begin
assign(fi,tfi);reset(fi);
assign(fo,tfo);rewrite(fo);
Nhap;
init;
xuly;
close(fi);
close(fo) ;
end.
IV. Một số bài tập vận dụng

8. Bài tập 1: BOCSOI13


Bước vào tiểu học, Bé Bi được cô giáo chủ nhiệm cho làm lớp trưởng. Nhân dịp kỷ
niệm ngày thành lập trường, Bé Bi tổ chức cho cả lớp chơi 1 trò chơi sau:
Có N đống sỏi xếp thành một hàng, đống thứ i có Ai viên sỏi. Ta có thể ghép hai đống
sỏi bất kỳ thành một đống và mất một chi phí bằng 5% tổng hai đống sỏi đó. Hãy tìm cách
ghép N đống sỏi này thành một đống với chi phí là nhỏ nhất.
Ví dụ: Nếu chúng ta có 4 đống sỏi với số lượng sỏi là 10, 11, 12 và 13.
- Bước 1: Ghép 2 đống 10 và 11 thành 1 đống có số lượng 21 (chi phí là 1.05)
- Bước 2: Ghép đống 21 vừa thu được với đống 12 thành đống có số lượng 33 (chi
phí 1.65)
- Bước 3: Ghép đống 33 vừa thu được với đống 13 thành 1 đống cuối cùng có số
lượng sỏi là 46 (chi phí 2.3)
- Vậy tổng chi phí là 5.00. Tuy nhiên đây không phải là phương án ghép đống tối ưu,
chúng ta có phương án ghép 4 đống này thành 1 đống với chi phí nhỏ nhất là 4.60.
Các bạn hãy tìm giúp Bé Bi phương án chơi tối ưu nhé!
INPUT:BOCSOI13.INP:
- Dòng 1: Số nguyên dương N là số đống sỏi.
- Dòng tiếp theo, ghi N số nguyên dương, tương ứng là số lượng sỏi trong từng đống.
Số lượng sỏi không vượt quá 10.000.
OUTPUT:BOCSOI13.OUT:
- Ghi 1 số thực duy nhất là chi phí nhỏ nhất phải trả để ghép N đống sỏi thành 1
đống. Kết quả ghi dưới dạng 2 chữ số sau dấu thập phân.
Ví dụ:
BOCSOI13.INP BOCSOI13.OUT
4 4.60
10 11 12 13

30
2 0.10
11

9. Bài tập 2: Kế hoạch làm bài


Nobita được giao n bài tập về nhà đánh số từ 1 tới n. Mỗi bài tập cần đúng 1 đơn vị
thời gian để làm và tại mỗi thời điểm, Nobita chỉ có thể làm được 1 bài tập. Bài tập thứ I
cần hoàn thành không muộn hơn thời điểm ti và nếu bài thứ I bị nộp muộn thì Nobita sẽ bị
thày giáo cho pi điểm 0.
Giả sử Nobita định làm bài tập từ thời điểm a đến hết thời điểm b. Hãy giúp Nobita lên
kế hoạch làm bài tập để số điểm 0 phải nhận là ít nhất.
INPUT: PENALTY.INP
- Dòng 1: Chứa 3 số nguyên dương n ;
- N dòng tiếp theo, dòng thứ I chứa hai số nguyên dương ti, pi
OUTPUT: PENALTY.OUT
Một số duy nhất là số điểm 0 tối thiểu phải nhận
Ví dụ:
PENALTY.INP PENALTY.OUT Giải thích
5 1 4 25 Làm bài 1 từ thời điểm 1 đến thời điểm 2.
2 100
Làm bài 4 từ thời điểm 2 đến thời điểm 3.
2 20
4 5 Làm bài 5 từ thời điểm 3 đến thời điểm 4.
4 10 Bài 2 và bài 3 bị nộp muộn
4 6

10. Bài tập 3: CATUN


Tại vương quốc HT có N thị trấn đánh số từ 1 đến N và M con đường nối 2 thị trấn bất
kỳ. Trong số N thị trấn thì có K thị trấn là pháo đài chống giặc ngoại xâm. Khi có giặc
ngoại xâm, 1 pháo đài bất kỳ sẽ bảo vệ những thị trấn gần nó nhất. Nếu co 1 thị trấn nào
đó có khoảng cách tới 2 pháo đài bằng nhau thì thị trấn này được bảo vệ bởi pháo đài có
chỉ số nhỏ hơn?
INPUT: CATUN.IN
- Dòng 1: N, M và K (
- Dòng 2: K số nguyên dương là chỉ số của K pháo đài
- M dòng tiếp theo, mỗi dòng ghi 3 số x, y, z có nghĩa là có con đường 2 chiều nối
thị trấn x với thị trấn y có chiều dài là z.
OUTPUT: CATUN.OUT
- Ghi N số nguyên dương, số thí I là chỉ số của pháo đài bảo vệ thị trấn i. Nếu thị trấn
I không được bảo vệ bởi pháo đài nào thì ghi số 0
Ví dụ:
CATUN.IN CATUN.OUT

31
8 9 2 5 0 5 0 0 5 0 2
2 5
1 3 6
1 5 3
1 6 1
2 3 9
5 6 5
6 8 7
3 6 2
4 7 1000
2 8 5
Time limit: 0.2s

11.Bài tập 3: Barbar


Sau cuộc chiến tranh thần thánh, HT bị bắt giam vào ngục tối là một bảng cỡ R*C (R dòng
và C cột). Tại một ô có thể di chuyển sang 4 ô kề cạnh. Trong ngục có 1 số ô là có thể di
chuyển tự do, một số ô là tường không thể di chuyển qua và đặc biệt một số ô bị chiếm giữ
bởi các con rồng lửa (không thể đi lại gần nếu không muốn bị cháy thành than). Bạn hãy
giúp HT di chuyển qua ngục tối sao cho khoảng cách ngắn nhất đến các con rồng lửa là
lớn nhất?
INPUT: BARBAR.IN
- Dòng 1: R và C (
- R dòng tiếp theo, mỗi dòng ghi C ký tự biểu diễn ngục tối.
“.” Ô di chuyển tự do
“*” Tường
“D” Rồng lửa
“I”: Vị trí xuất phát của HT
“O”: Vị trí thoát khỏi ngục tối
OUTPUT: BARBAR.OUT
- Một số duy nhất là giá trị lớn nhất của khoảng cách ngắn nhất đến các con rồng lửa
khi HT di chuyển ra khỏi ngục tối. Nếu không có cách di chuyển ghi -1
Ví dụ:
BARBAR.IN BARBAR.OUT Giải thích

10 10 2 ..........
.......... .Iooo.D...
.I....D... ....o.....
.......... ..D.o.D...
..D...D... .*..oo....
.*........ D*...ooooo
D*........ *...D....o
*...D..... ..****...o
..****.... ...Ooooooo
...O...... ..........
..........

32
Time limit: 0.4s

12. Bài tập 4:Cầu cảng


Một cầu cảng có m cầu cảng để tiếp nhận các tàu cập bến. Tại một thời điểm, mỗi cầu
cảng chỉ có thể tiếp nhận không quá 1 tàu. Ban đầu các cầu cảng đều trống và có n tàu xin
đăng ký cập bến, tàu thứ i muốn đậu ở cảng ngày sau thời điểm tới hết thời điểm . Có
thể coi thời gian tàu thứ i muốn đậu ở cảng là một khoảng trên trục thời gian. Tàu
đã vào cầu cảng nào thì sẽ đậu ở đó trong suốt thời gian nằm cảng.
Yêu cầu : Hãy cho biết với m cầu cảng đã cho, có thể tiếp nhận được tối đa bao nhiều tàu
và chỉ ra lịch trình tiếp nhận tại mỗi cầu cảng ?
INPUT:SEAPORTS.INP
- Dòng 1: Chứa 2 số nguyên dương
- N dòng tiếp theo, dòng thứ i chứ 2 số nguyên
OUTPUT:SEAPORTS.OUT
- Dòng 1 : Ghi số lượng tàu được tiếp nhận phục vụ
- Dòng 2 : Ghi n số nguyên, số thứ i là số hiệu cầu cảng sẽ tiếp nhận tàu thứ i trong
trường hợp tàu thứ i được tiếp nhận, còn nếu tàu thứ i không được tiếp nhận thì số
thứ i là 0.
Ví dụ :
SEAPORTS.INP SEAPORTS.OUT
25 4
03 11220
35
02
25
14

13. Bài tập 5:K TỔNG BÉ NHẤT


Cho 2 dãy số nguyên A và B. Với mọi số A[i] thuộc A và B[j] thuộc B (1 ≤ Ai, Bi ≤ 109 )
người ta tính tổng nó. Tất cả các tổng này sau khi được sắp xếp không giảm sẽ tạo thành
dãy C. Nhiệm vụ của bạn là: Cho 2 dãy A, B. Tìm K số đầu tiên trong dãy C
INPUT: KMIN.INP
- Dòng đầu tiên gồm 3 số: M, N, K (1≤M, N, K ≤ 50.000)
- M dòng tiếp theo gồm M số mô tả dãy A
- N dòng tiếp theo gồm N số mô tả dãy B
OUTPUT:KMIN.OUT
- Gồm K dòng tương ứng là K phần tử đầu tiên trong dãy C.
Ví dụ:
KMIN.INP KMIN.OUT
446 3
33
1 4
2 4
3 5
4 5
2 5
3
4
5
Time limit: 1s

14. Bài tập 6: BINLADEN


Trùm khủng bố Bin Laden trốn trong 1 căn hầm được đào sâu xuống mặt đất M tầng,
mỗi tầng có N phòng. Các phòng được ngăn cách bằng các cửa rất khó phá. Các phòng có
cửa xuống phòng ngay phía dưới và 2 phòng ở 2 bên. Từ trên mặt đất có N cửa xuống N
phòng tầng -1. Bin Laden ở tầng dưới cùng (tầng -M) phòng thứ N (phòng ở bên phải
nhất). Mỗi cửa được làm bằng một kim loại khác nhau với độ dày khác nhau nên việc phá
cửa cần thời gian khác nhau.
Bạn hãy tìm cách đi từ mặt đất xuống phòng của Bin Laden nhanh nhất không hắn thoát
mất.
INPUT: BINLADEN.INP
- Dòng 1 ghi M và N
- Dòng 2 đến 2M + 1, dòng chẵn ghi N số, dòng lẻ ghi N - 1 số là chi phí để phá cửa.
Chi phí của các cánh cửa thuộc [0, 1000].
OUTPUT: BINLADEN.OUT
- Ghi ra 1 số là thời gian nhỏ nhất để đến được phòng của Bin Laden
Ví dụ:
BINLADEN.INP BINLADEN.OUT
42 44 +--99--+--10--+
| | |
99 10 | 1 |
1 | | |
10 99 +--10--+--99--+
| | |
1 | 1 |
99 10 | | |
1 +--99--+--10--+
| | |
10 99 | 1 |
1 | | |
+--10--+--99--+
| | |
| 1 |
| | |
+------+------+
Đi theo đường zigzac

34
V. Tài liệu tham khảo
1. Nguồn bài tập: Thày Nguyễn Thanh Tùng, Thày Lê Minh Hoàng, Rumania OI,
Poland OI, Russian OI, vn.spoj.pl.
2. Giải thuật và lập trình – T.S Lê Minh Hoàng – ĐHSP Hà Nội
3. Website: www.infoarena.ro
4. Test và solution các Thày cô có thể liên hệ với tác giả để download.

35

You might also like