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.. Hai anh em phân tích bảng chu kỳ sinh học của Alexei và thấy rằng
Trang 1MỤC LỤC
MỤC LỤC 1
Khái niệm 2
Các thao tác thường dùng đối với Heap Max 2
Khai báo 2
UpHeap 2
DownHeap 3
Push 4
Pop 4
Dijkstra Heap 5
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
Bài tập4:Tiểu thuyết trinh thám 13
Bài tập 5: Cezar 18
Bài tập 6:Toy Cars 22
Bài tập 7:BASE3 26
Một số bài tập vận dụng 30
Bài tập 1: BOCSOI13 30
Bài tập 2: Kế hoạch làm bài 31
Bài tập 3: CATUN 31
Bài tập 3: Barbar 32
Bài tập 4:Cầu cảng 33
4.Bài tập 5:K TỔNG BÉ NHẤT 33
Bài tập 6: BINLADEN 34
Tài liệu tham khảo 35
Trang 2CHUYÊN ĐỀ: CẤU TRÚC DỮ LIỆU HEAP 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)
Các thao tác thường dùng đối với Heap Max
Trang 3Procedure 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;
DownHeap
Nếu 1 nút nhỏ hơn nút con thì đẩy nó xuống dưới:
Trang 4Procedure 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
Inc(nHeap); // Tăng số phần tử của Heap
Heap[nHeap] := x; // Thêm x vào Heap
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
Trang 5{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:
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;
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
Trang 6end;
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
- 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 ?
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
Trang 7if (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;
Trang 10If (heap[j] >heap[j+1]) and (j <nheap) then inc(j);
If heap[i] > heap[j] then
Trang 11HT 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à phi công cao tuổi nhất) HT airline cần chính xác
2
N
phi hành đoàn, mỗi phi hành đoàn
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ụ
Để 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
2
N
phi 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
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ụ
Trang 12Arr1 = array[1 max] of longint;
begin
if (i = 1) or (h[i] < h[i div 2]) then exit;
if h[i div 2] < h[i] then
Trang 13Bà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
Trang 14Thự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 r i 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ó p i 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 c i
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
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
Trang 15b 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
if (i = 1) or (h[i] > h[i div 2]) then exit;
if h[i div 2] > h[i] then
Trang 17push(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;
nhap;
Trang 18Mỗ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?
– 5-7, 7-8, 8-10
đường miễn phí
– 8 là thượng
việnChi phí tối thiểu là:
9 10
13
Trang 19Time 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
Trang 21Repeat
root := child div 2;
If (root=0) or (cost[heap[root]] <= cost[u]) then break; heap[child] := heap[root];
cost[ke[i]] := cost[ke[i]] + cost[v];
If deg[ke[i]] = 1 then push(ke[i]);
Trang 22Bà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)
Trang 23Mẹ 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)
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;
var
Trang 24var j:longint;
Trang 25j:=i*2;
if j>nheap then exit;
if (d[a[j]]<d[a[j+1]])and(j<nheap) then inc(j);
Trang 26{ -} procedureinkq;
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:
13
Giải thích
N = 2020001001001
Time limit/test: 0.4
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)
L
Trang 27Dễ 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à
Trang 28h[i] := h[i div 2] ;
h2[i] := h2[i div 2];
if d[x,y] <= d[h[j],h2[j]] then break ;
Trang 30Ví 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
Trang 31Bà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
25 Làm bài 1 từ thời điểm 1 đến thời điểm 2
Làm bài 4 từ thời điểm 2 đến thời điểm 3
Làm bài 5 từ thời điểm 3 đến thời điểm 4
Bài 2 và bài 3 bị nộp muộn
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