You are on page 1of 15

BÀI TẬP - ỨNG DỤNG SẮP XẾP, TÌM KIẾM

Bài 1: Dãy con


Cho một dãy số nguyên dương a1,a2,...,aN (10 < N < 100 000), ai <=10000 với mọi i=1..N và
một số nguyên dương S (S < 100 000 000).
Yêu cầu : Tìm độ dài nhỏ nhất của dãy con chứa các phần tử liên tiếp của dãy mà có tổng các
phần tử lớn hơn hoặc bằng S.
Dữ liệu vào: Đọc từ file SUB.INP gồm 2 dòng, dòng 1 chứa N và S ở dòng đầu. Dòng 2 chứa
các phần tử của dãy.
Dữ liệu ra: Kết quả ghi vào file SUB.OUT, chứa độ dài của dãy con tìm được.
Ví dụ :
SUB.INP SUB.OUT
10 15 2
5 1 3 5 10 7 4 9 2 8
* Hướng dẫn giải:
Bài toán này có thể giải theo 3 cách sau:
Cách 1: dễ dàng giải bài toán với 1 cách làm trâu bò là xét 2 vòng lặp lồng nhau để tìm
tất cả các tổng của các đoạn con đồng thời kết hợp tìm đoạn con có tổng >= S và có số
phần tử ít nhất. Độ phức tạp là O(N2)
Cách 2: Sử dụng phương pháp tìm kiếm nhị phân để giải bài toán:
Gọi T[i] là tổng của các số A[1] đến A[i].
Vì A[i] là các số dương => Dãy T là dãy tăng dần.
Khi đó ta sẽ tiến hành tìm kiếm nhị phân trên dãy T như sau:
* Xét T[i]:
d = 1, c = i-1,
g = (d + c) div 2
Nếu T[i] – T[g] >= S thì kq = min(kq, i – g) và tìm kq tiếp tục ở đoạn bên phải T[g]
Nếu T[i] – T[g] < S thì tìm kq ở đoạn bên trái T[g].
Độ phức tạp là O(NlogN)
Cách 3: Bài toán có thể giải với độ phức tạp là O(N).
Xét tổng đoạn T = A[d] + A[d+1] + A[d+2] + …+ A[c]
Nếu T < S thì cần bổ sung thêm giá trị của A[c+1] vào tổng để T có thể >= S

1
Nếu T <= S thì cập nhật lại kq, vì kết quả là đoạn ngắn nhất nên nếu loại bỏ A[d] đi để
được đoạn ngắn hơn đoạn trước đó.
Bài 2: Đếm tam giác
Cho 3 dãy số dương A, B, C cùng có N phần tử. Hãy đếm xem có bao nhiêu bộ 3 số A[i], B[j]
và C[k] mà 3 số này là 3 cạnh của 1 tam giác.
Dữ liệu vào: từ file TRIANGLE.INP với cấu trúc:
- Dòng đầu chứa số nguyên n (n <= 1000)
- Dòng thứ hai chứa các số A1, A2, ..., An.
- Dòng thứ ba chứa các số B1, B2, ..., Bn.
- Dòng thứ tư chứa các số C1, C2, ..., Cn.
Các số ai, bi, ci đều không vượt quá 104 và được ghi cách nhau bởi dấu cách.
Dữ liệu ra: file văn bản TRIANGLE.OUT gồm một số S duy nhất là số lượng bộ ba số tìm
được.
TRIANGLE.INP TRIANGLE.OUT TRIANGLE.INP TRIANGLE.OUT
2 2 3 8
23 231
31 449
47 852
* Hướng dẫn:
Chúng ta có thể giải bài này bằng 2 cách như sau:
Cách 1: Bài toán được giải một cách dễ dàng bằng phương pháp vét cạn: dùng 3 vòng lặp
lồng nhau để xét từng bộ ba số (ai, bj, ck).
Độ phức tạp là O(N3).
Cách 2
Ta có cách giải với độ phức tạp là O(N2*logN) như sau:
- Trước hết ta giải bài toán phụ như sau: Cho dãy không giảm A, và hai số x < y. Hãy sử dụng
phương pháp tìm kiếm nhị phân để đếm xem có bao nhiêu số A[i] thỏa mã x < A[i] < y.
Giải:
Bước 1: Viết hàm TimKiem1(x): để tìm vị trí i1 nhỏ nhất mà tại đó A[i1] > x.
Bước 2: Viết hàm TimKiem2(y): để tìm vị trí i2 lớn nhất mà tại đó A[i2] < y.
Kq = i2 – i1 + 1
Dựa vào bài toán phụ ở trên ta giải bài toán TRIANGLE như sau:
2
Ta có nhận xét: Ba số a[i], b[j], c[k] là 3 cạnh của một tam giác khi và chỉ khi thỏa mãn hệ
thức:
|a[i] – b[j]| <c[k] < a[i] + b[j]
Như vậy là ta chỉ cần xét 2 số a[i] và b[j] và đếm xem có bao nhiêu số C[k] thỏa mãn hệ thức
trên.
Bước 1. Sắp xếp mảng C tăng dần
Bước 2. Sử dụng 2 vòng lặp lồng nhau duyệt mảng A và B
- Nếu |ai-bj| < c[N] thì xét tiếp
o Nếu ai+bj > c1 thì
Tìm kiếm số |ai – bj| trong dãy c bằng phương pháp tìm kiếm nhị phân
được vị trí l
Tìm kiếm số ai+bj trong dãy c bằng phương pháp tìm kiếm nhị phân
được vị trí k
Nếu k >= l thi dem = dem + k – l + 1;
Bài 3. Trò chơi với dãy số
Hai bạn học sinh trong lúc nhàn rỗi nghĩ ra trò chơi sau đây. Mỗi bạn chọn trước một dãy số
gồm n số nguyên. Giả sử dãy số mà bạn thứ nhất chọn là: b1, b2, ..., bn còn dãy số mà bạn thứ
hai chọn là c1, c2, ..., cn
Mỗi lượt chơi mỗi bạn đưa ra một số hạng trong dãy số của mình. Nếu bạn thứ nhất đưa ra số
hạng bi (1 <= i <= n), còn bạn thứ hai đưa ra số hạng cj (1 <= j <= n) thì giá của lượt chơi đó
sẽ là |bi+cj|.
Ví dụ: Giả sử dãy số bạn thứ nhất chọn là 1, -2; còn dãy số mà bạn thứ hai chọn là 2, 3. Khi
đó các khả năng có thể của một lượt chơi là (1, 2), (1, 3), (-2, 2), (-2, 3). Như vậy, giá nhỏ
nhất của một lượt chơi trong số các lượt chơi có thể là 0 tương ứng với giá của lượt chơi (-2,
2).
Yêu cầu: Hãy xác định giá nhỏ nhất của một lượt chơi trong số các lượt chơi có thể.
INPUT: vào từ file văn bản SEQGAME.INP
- Dòng đầu là số nguyên dương (1 <= n <= 105)
- Dòng thứ hai chứa các số là dãy b (|bi| <= 109)
- Dòng thứ hai chứa các số là dãy c (|ci| <= 109)
OUTPUT: ghi ra file văn bản SEQGAME.OUT giá trị nhỏ nhất tìm được
Ví dụ:
3
Ràng buộc: 60% số test ứng với 60% số điểm có 1<= n <= 1000
Hướng dẫn giải
Bài toán có thể giải bằng 3 cách:
Cách 1: Sử dụng 2 vòng lặp lồng nhau để tìm giá trị nhỏ nhất. Độ phức tạp là O(N2)
Cách 2: Sử dụng phương pháp tìm kiếm nhị phân như sau:
Bước 1: Sắp xếp dãy A tăng dần
Bước 2: xét các giá trị B[i].
Xét các giá trị A[g] (g = (d + c) div 2 với d = 1, c = N)
Min := A[g] + B[i]
- Nếu A[g] + B[i] < 0 thì phải tăng g lên để tổng gần 0 hơn khi đó d := g + 1
- Nếu A[g] + B[j] > 0 thì phải giảm g xuống để tổng gần 0 hơn khi đó c := g – 1.
- Nếu Min = 0 thì thoát
Kq = |Min|
Độ phức tạp là O(NlogN)
Cách 3: Cũng có thể giải quyết bài toán với độ phức tạp là O(NlogN) như sau:
Nhận xét: |Ai + Bj| min khi giá trị này càng gần 0 càng tốt.
Dựa vào nhận xét trên ta giải tiếp như sau:
Bước 1: Sắp xếp 2 dãy tăng dần
Bước 2: Xét dãy A từ đầu dãy i = 1 và dãy B từ cuối dãy j = N
Min := |A1 + Bn|
Lặp khi i <= N và 1 <= j
- Nếu Ai + Bj = 0 thì min = 0 và thoát
- Nếu Ai + Bj < 0 thì cần phải tăng i, vì nếu giảm j thì Ai + Bj sẽ càng < 0 hơn nữa
- Nếu Ai + Bj > 0 thì cần phải giảm j, vì nếu tăng i thì Ai + Bj sẽ càng > 0 hơn nữa.
Bài 4. Bộ số trung bình cộng.
Cho 4 dãy số A, B, C, D có N phần tử dương nhỏ hơn hoặc bằng 104. Bộ 4 số (i, j, k, l) gọi là
bộ số trung bình cộng nếu D[l] = (A[i]+B[j]+C[k])/3.
Yêu cầu: hãy đếm số lượng bộ số trung bình cộng trong 4 dãy trên.
Dữ liệu vào: TBC.INP
4
- Dòng 1: chứa 1 số N (N <= 500)
- Dòng 2: chứa N số thuộc dãy A
- Dòng 3: chứa N số thuộc dãy B
- Dòng 4: chứa N số thuộc dãy C
Kết quả: ghi ra file TBC.OUT 1 số duy nhất là số S là số bộ số trung bình cộng tìm được.
Ví dụ:
TBC.INP TBC.OUT
3 1
345
123
234
456
Giải thích: chỉ có 1 bộ số trung bình cộng là (5 + 3 + 4)/3 = 4.
 Hướng dẫn giải:
Cách 1: xét 4 vòng lặp lồng nhau để đếm bộ số trung bình cộng. Độ phức tạp là O(N4)
Cách 2: Có thể giải quyết bài toán với độ phức tạp là O(N2logN2)
Từ (A[i] + B[j] + C[k])/3 = D[l] => A[i] + B[j] = 3*D[l] – C[k].
- Bước 1: Tạo dãy AA bao gồm mọi phần tử là tổng của các cặp A[i] và B[j]. Khi đó AA có
N2 phần tử.
- Bước 2: Tạo dãy BB bao gồm các phần tử thỏa mãn 3*D[l] – C[k] > 0. Khi đó BB có d phần
tử (d <= N2)
- Bước 3: Sắp xếp dãy AA và BB tăng dần.
- Bước 4: Tạo dãy AAA là các phần tử của mảng AA chỉ xuất hiện 1 lần (VD: AA =
(1,1,3,3,4,4,4) thì AAA = (1,3,4)), TSA[AA[i]] là tần số xuất hiện các phần tử của phần tử
AA[i] trong dãy, SlgA là số phần tử của mảng AAA.
Dãy BBB, TSB, và biến SlgB có tính chất tương tự.
- Bước 5: Xử lý:
i := 1; j := 1; d := 0;
Lặp khi i <= SlgA và j <= SlgB
Nếu AAA[i] < BBB[j] thì inc(i); {tăng i để AAA[i] gần BBB[j] không thể tăng
j vì nếu tăng j thì AAA[i] sẽ càng xa BBB[j] hơn}
Nếu i > SlgA thì break;
5
Nếu AAA[i] > BBB[j] thì inc(j); {tăng j để BBB[j] gần BAA[i]} không thể tăng
i vì nếu tăng i thì AAA[i] sẽ càng xa BBB[j] hơn}
Nếu j > SlgB thì break;
Nếu AAA[i] = BBB[j] thì d := d + TSA[AAA[i]] * TSB[BBB[j]];
Kết thúc lặp
Kq = d;
Bài 5. Hàng cây (nguồn bài: http://vn.spoj.pl/problems/FIRS).
Biên giới giữa hai nước Ngược và Xuôi có dạng một đoạn thẳng với độ dài N-1 mét.
Vua nước Ngược vì muốn che giấu bí mật quốc gia đã trồng trên đường biên giới N cây tán
lá xum xuê (các cây cách đều nhau với khoảng cách một mét). Vua nghĩ rằng nhờ hàng cây
này mà các điệp viên của vua nước Xuôi không thể do thám nước mình được. Để chăm sóc
các cây này, vua sai một người làm vườn mỗi buổi sáng chọn cây thưa lá nhất (có số lá ít
nhất) và tưới một loại phân bón đặc biệt (nếu có nhiều cây thưa lá nhất thì người làm vườn sẽ
chọn cây đầu tiên). Phân bón có bán kính tác dụng là 1 mét (nghĩa là sẽ tác dụng lên từ 1 đến
3 cây).
Tuy nhiên, vua nước Xuôi quyết định chống lại chiến lược này và thuê một người làm vườn
khác. Mỗi buổi chiều, người làm vườn này tưới phân bón lên cùng cái cây đã được người làm
vườn nước Ngược tưới vào buổi sáng, nhưng bằng một loại phân bón khác. Loại phân bón
này làm chết tất cả các cây trong bán kính 1 mét!
Bạn được bộ trưởng tài chính của vua nước Xuôi thuê để giúp tính xem sau bao nhiêu ngày
thì tất cả các cây đều bị chết. Hãy lập trình tính giá trị này.
Dữ liệu: đọc từ file FIRS.INP:
Dòng đầu tiên chứa số lượng cây N (1 <= N <= 105). Dòng thứ hai chứa N số nguyên ai (1 <=
ai <= 105) - số lượng lá trên các cây (theo thứ tự chúng được trồng từ trái sang phải).
Kết quả: ghi ra file FIRS.OUT: In ra số ngày mà sau đó tất cả các cây đều bị chết.
FIRS.INP FIRS.OUT
3 1
322
3 2
223
* Hướng dẫn giải:
Đây là một bài khá đơn giản về thuật toán sắp xếp.
6
Vì người tưới chọn cây có lá ít nhất bên trái ngoài cùng để tưới, và người làm chết cây chọn
cây người tưới để làm chết cây, nên đơn giản nhất là sắp xếp lại số lá của các cây theo thứ tự
tăng dần.
Trong quá trình sắp xếp QuickSort cần chú ý:
- Điều kiện để i tăng là (A[i].gt < x.gt) or ((A[i].gt = x.gt) and (A[i].vt < x.vt))
- Điều kiện để j giảm là (A[j].gt > x.gt) or ((A[j].gt = x.gt) and (A[j].vt > x.vt))
Trong đó: A[i].gt là số lá của cây thứ i, A[i].vt là vị trí ban đầu của cây thứ i. x = A[(d+c) div
2]
Dùng mảng dd[1..n] để đánh dấu cây con sống hay đã chết.
- Bước 1: Sắp xếp
- Bước 2: Nếu cây thứ A[i].vt còn sống (tức là dd[A[i].vt] = true) thì cho cây này chết
đồng thời những cây bên cạnh cũng sẽ chết, rồi tăng biến đếm.
Bài 6. Cách nhiệt (nguồn bài: http://vn.spoj.pl/problems/INSUL/).
Cho một dãy N viên gạch lần lượt có độ cách nhiệt là các số a1.. aN. Nếu xếp lần lượt các viên
gạch theo trình tự đó thì độ cách nhiệt cả khối là a1 + a2 + ... + aN + max(0, a2 - a1) + max(0,
a3 - a2) + ... + max(0, aN - aN - 1). Nhiệm vụ của bạn là tìm cách xếp sao cho độ cách nhiệt của
cả khối là lớn nhất có thể.
Dữ liệu: Đọc từ file INSUL.INP
Dòng đầu ghi số nguyên dương N (0 < n <= 10^5).
N dòng sau mỗi dòng ghi một số ai ( 1 <= i <= N và 1 <= ai <= 10000).
Kết quả: ghi ra file INSUL.OUT: Ghi trên một dòng kết quả là nhiệt độ lớn nhất tìm được
Ví dụ
INSUL.INP INSUL.OUT
4 24
5
4
1
7
* Hướng dẫn giải:
- Bước 1: Tính tổng S = A1 + A2 + ... + AN
- Bước 2: Sắp xếp dãy tăng dần
- Bước 3: S := S + AN – A1 + AN-1 – A2 + AN-2 – A3 + ...
7
Bài 7 Ghép số lớn (Nguồn bài: http://vn.spoj.pl/problems/NUMCON/).
Vaxia đã viết được một số lớn trên một cuộn giấy dài và muốn khoe với anh trai Petia về
thành quả vừa đạt được. Tuy nhiên, khi Vaxia vừa ra khỏi phòng để gọi anh trai thì cô em
Kachia chạy vào phòng và xé rách cuộn giấy thành một số mảnh. Kết quả là trên mỗi mảnh
có một hoặc vài kí số theo thứ tự đã viết.
Bây giờ Vaxia không thể nhớ chính xác mình đã viết số gì. Vaxia chỉ nhớ rằng đó là một số
rất lớn.
Để làm hài lòng cậu em trai, Petia quyết định truy tìm số nào là lớn nhất mà Vaxia đã có thể
viết lên cuộn giây trước khi bị xé. Bạn hãy giúp Petia làm việc này.
Dữ liệu vào: từ file NUMCON.INP:
Ghi một hoặc nhiều dòng. Mỗi dòng ghi một dãy kí số. Số dòng không vượt quá 100. Mỗi
dòng ghi từ 1 đến 100 kí số. Bảo đảm rằng có ít nhất một dòng mà kí số đầu tiên khác 0.
Dữ liệu ra: ghi ra file NUMCON.INP: số lớn nhất đã có thể viết trên cuộn giấy trước khi
bị xé rách.
Ví dụ:

NUMCON.INP NUMCON.OUT
2 66220004
20
004
66
* Hướng dẫn giải:
Săp xếp dãy S giảm dần như sau:
Nếu (Si ghép trước Sj) lớn hơn (Si ghép sau Sj) thì Si sẽ đứng trước Sj.
Kq cuối cùng là dãy S sau khi sắp xếp ghép lại.
Bài 8. Số ngẫu nhiên.
Cho dãy số nguyên a1,a2,..,an.Số ai được gọi là số k-ngẫu nhiên của dãy nếu trong k số hạng
liên tiếp bất kì của dãy đều có ít nhất một số hạng bằng ai và k là số nguyên nhỏ nhất thỏa
mãn điều kiện này.
Ví dụ: Dãy 1,2,3,1,2,2. Số 1 là số 3-ngẫu nhiên; số 2 là số 3-ngẫu nhiên; số 3 là số 4-ngẫu
nhiên.
Yêu cầu: Tìm k nhỏ nhất để trong dãy có số k – ngẫu nhiên.
8
Input: Cho trong tệp văn bản RANNUM.INP như sau:
- Dòng đầu ghi số nguyên dương n (N<=10^5)
- Dòng thứ 2 ghi n số nguyên a1,a2,..an.(|ai|<=10^3).
Output: Ghi trong tệp văn bản RANNUM.OUT gồm một số k tìm được thỏa mãn yêu cầu
bài toán.
Ví dụ:
RANNUM.INP RANNUM.OUT
6 3
123122
* Hướng dẫn giải:
Ta sẽ giải bài toán bằng phương pháp tìm kiếm nhị phân: Độ phức tạp là O(NlogN)
Bước 1: Khởi tạo K = N (Mọi số trong dãy đều là số N – ngẫu nhiên)
Bước 2:
d := 1; c:= k
While d <= c do
Begin
g := (d+c) div 2;
if OK(g) then {Nếu dãy có số g – ngẫu nhiên}
Begin
kq := g; {lưu lại kq}
c := g – 1; {giảm g}
end
else {ngược lại thì tăng g} d := g + 1;
end;
Vấn đề đặt ra là hàm OK(k) – Kiểm tra dãy có số k – ngẫu nhiên phải xử lý thế nào?
Muốn kiểm tra A[i] có phải là số k – ngẫu nhiên hay không thì chỉ việc kiểm tra trong mọi
đoạn k số liên tiếp có xuất hiện A[i] không.
Có thể sử dụng thuật toán với độ phức tạp là O(N) để giải quyết bài toán.
Ta thấy 1 trong k số đầu tiên của dãy sẽ là số k – ngẫu nhiên.
Vì vậy sẽ dùng thêm mảng dem[-1000 .. 1000] để đếm tần số xuất hiện các của k phần tử đầu
tiên trong dãy, khi đó dem[A[i]] > 0 nghĩa là A[i] có thể là số k – ngẫu nhiên.
Duyệt 1 vòng lặp: i từ k+1 đến N:
9
+ if dem[A[i]] = 0 then dec(dem[A[i-k]])
{Nếu số A[i] không xuất hiện trong k số từ A[i – k] tới A[i – 1] thì A[i] không là số k ngẫu
nhiên – thì giảm dd[A[i-k]] 1 đơn vị để loại số A[i-k] này ra khỏi dãy có k số liên tiếp}
+ if (dem[A[i]] > 0) and (dem[A[i]] <> dem[A[i-k]] then
Begin
inc(dem[A[i]]);
dec(dem[A[i-k]]);
end;
{Nếu A[i] xuất hiện trong dãy A[i – k] tới A[i – 1] thì A[i] có thể là số k – ngẫu nhiên và
trong dãy A[i-k+1] đến A[i] có A[i] thì tăng dem[A[i]]}
Duyệt lại lần nữa để kiểm tra xem có dd[i] > 0 hay không? Nếu có thì dãy có số k – ngẫu
nhiên.
Bài 9. Những hình nhân nhảy múa.
Những hình nhân nhảy múa là một loại mật mã bí ẩn đã xuất hiện trong một câu chuyện về
thám tử Sherlock Homes. Ngày nay, người ta vẫn còn dùng loại mật mã này, nhưng các hình
nhân được truyền đi bằng hình ảnh qua Internet và khó giải mã hơn. Chúng sẽ tự động biến
đổi đi theo thời gian để trở nên khó nhận dạng so với mật mã ban đầu. Các hình nhân có chiều
cao khác nhau, mỗi hình nhân có thể quay lên phía trên hoặc quay xuống phía dưới.

Để tiện lợi, ta quy ước chiều cao của mỗi hình nhân là số dương nếu quay đầu lên trên và số
âm nếu quay đầu xuống dưới. Sau mỗi giây, các hình nhân sẽ biến đổi như sau. Ba hình nhân
liên tiếp bất kỳ sẽ được chọn. Chiều cao của hai hình nhân ở bên trái và phải sẽ được cộng
thêm một lượng bằng chiều cao kể cả dấu của hình nhân ở giữa. Sau đó, hình nhân ở giữa sẽ
quay ngược đầu lại. Hình dưới đây minh họa sự biến đổi của ba hình nhân liên tiếp:

Biết rằng ban đầu, các hình nhân đều quay lên phía trên.

10
Biết dãy các hình nhân tại một thời điểm nào đó, bạn hãy xác dịnh dãy hình nhân ban đầu,
hoặc thông báo dãy hình nhân không hợp lệ, nếu không tìm được dãy hình nhân ban đầu hoặc
dãy hình nhân ban đầu không phải là duy nhất.
Dữ liệu: Đọc từ file DANCING.INP:
Dòng 1: chứa số nguyên dương N, là số lượng hình nhân trên dãy.
Dòng 2: chứa N số nguyên a1, a2, a3, ..., an. Trong đó |ai| cho biết chiều cao của hình
nhân thứ i tại một thời điểm nào đó, ai > 0 nếu hình nhân quay đầu lên trên, và ai <
0 nếu hình nhân quay đầu xuống dưới.
Các hình nhân được đánh số thứ tự từ 1 đến N từ trái sang phải.
Kết quả: ghi ra file DANCING.OUT:
Gồm một dòng duy nhất chứa:
Số -1 nếu không tìm được dãy hình nhân ban đầu hoặc dãy hình nhân ban đầu
không phải là duy nhất.
Hoặc N số nguyên b1, b2, ..., bn là dãy hình nhân ban đầu, với cùng kiểu mô tả như
trong dữ liệu vào. b1, b2, ..., bn phải là số nguyên dương, vì các hình nhân ban đầu
đều quay lên trên.
Giới hạn
1 <= N <= 105.
1 <= |ai| <= 5000.
Ví dụ:
DANCING.INP DANCING.OUT
5 560 244 762 885 245
2451 -1647 -244 1006 1130
* Hướng dẫn giải:
Gọi S1[i] = S[1] + S[2] + ... + S[i]
Gọi S2[i] = S[N] + S[N-1] + ... + S[N-i+1]
Nếu S1[i] <= 0 thì không có cách biến đổi đoạn từ 1 -> i thành dãy ban đầu
Nếu S2[i] < 0 thì không có cách biến đổi đoạn N – i + 1 -> N thành dãy ban đầu.
Trong trường hợp luôn tồn tại dãy ban đầu ta xét tiếp:
Xét khi biến đổi bộ 3 số a[i-1], a[i] và a[i+1], ta có nhận xét:
S1'[i-1] = S1[i-2] + a[i-1] + a[i] = S1[i];
S1'[i]= S1[i]- a[i] = S1[i-1];
11
S1[i+1]= S1'[i] + a[i-1] + a[i]= S1[i+1];
Do nhận xét trên nên khi biến đổi như vậy thì S1[i] và S1[i-1] đổi chỗ cho nhau. Vì vậy công
việc bây giờ là Quicksort S1 trong O(NlogN) rồi chọn S1[i] – S1[i-1] với i >=2 là phần tử thứ
i của dãy kết quả ban đầu.
Bài 10. Đóng gói sản phẩm.
Ở đầu ra của một dây chuyền sản xuất trong nhà máy ZXY có một máy xếp tự động. Sau khi
kết thúc việc gia công trên dây chuyền, các sản phẩm sẽ được xếp vào các hộp có cùng dung
lượng M. Sản phẩm rời khỏi dây chuyền được xếp vào hộp đang mở (khi bắt đầu ca làm việc
có một hộp rỗng được mở sẵn) nếu như dung lượng của hộp còn đủ để chứa sản phẩm. Trong
trường hợp ngược lại, máy sẽ tự động đóng nắp hộp hiện tại, cho xuất xưởng rồi mở một hộp
rỗng mới để xếp sản phẩm vào. Trong một ca làm việc có n sản phẩm đánh số từ 1 đến n theo
đúng thứ tự mà chúng rời khỏi dây chuyền. Sản phẩm thứ i có trọng lượng là ai, i = 1, 2, …,
n. Ban Giám đốc nhà máy qui định rằng sản phẩm xuất xưởng của mỗi ca làm việc phải được
xếp vào trong không quá k hộp.
Yêu cầu: Hãy giúp người quản đốc của ca làm việc xác định giá trị M nhỏ nhất sao cho số
hộp mà máy tự động cần sử dụng để xếp dãy n sản phẩm xuất xưởng của ca không vượt quá
số k cho trước.
Dữ liệu: Vào từ file văn bản ZXY.INP:
• Dòng đầu tiên chứa hai số nguyên n và k, (1 <= k <= n <= 15000);
• Dòng thứ i trong n dòng tiếp theo chứa số nguyên dương ai (ai <= 30000), i =1, 2, …, n.
Các số trên một dòng cách nhau ít nhất một dấu cách.
Kết quả: Ghi ra file ZXY.OUT một số nguyên duy nhất là dung lượng của hộp.
Ví dụ:

*Hướng dẫn:
Bài toán này hoàn toàn có thể giải quyết được bằng phương pháp tìm kiếm nhị phân.
12
Gọi Max là trọng lượng lớn nhất trong n hộp, S là tổng trọng lượng n hộp.
Chúng ta sẽ chặt nhị phân trong đoạn từ Max -> S để tìm M (vì M chắc chắn nằm trong đoạn
từ Max -> S) như sau:
d := Max; c := S;
Lặp khi d <= c:
- M := (d + c)/2
- Dem = so lượng hộp trọng lượng M có thể chứa được n vật phẩm
- Nếu dem > k nghĩa là M quá nhỏ vì vậy cần tăng kích thước của M bằng cách chặt nhị
phân tìm M trong đoạn từ d = M + 1 -> c
- Ngược lại: M đã thỏa mãn có thể chứa được n vật phẩn, tuy nhiên M có thể chưa là giá
trị nhỏ nhất. Vì vây lưu lại giá trị kq = M, và tìm M có giá trị nhỏ hơn bằng cách chặt
nhị phân tìm M trong đoạn từ d -> c = M - 1
Bài 11: Robot cứu hỏa.
Trên một mạng lưới giao thông có n nút, các nút được đánh số từ 1 đến n và giữa hai nút bất
kỳ có không quá một đường nối trực tiếp (đường nối trực tiếp là một đường hai chiều). Ta gọi
đường đi từ nút s đến nút t là một dãy các nút và các đường nối trực tiếp có dạng:
s = u1, e1, u2,..., ui, ei, ui+1, ..., uk-1, ek-1, uk = t,
trong đó u1, u2, …, uk là các nút trong mạng lưới giao thông, ei là đường nối trực tiếp giữa nút
ui và ui+1 (không có nút uj nào xuất hiện nhiều hơn một lần trong dãy trên, j = 1, 2, …, k).
Biết rằng mạng lưới giao thông được xét luôn có ít nhất một đường đi từ nút 1 đến nút n.
Một robot chứa đầy bình với w đơn vị năng lượng, cần đi từ trạm cứu hoả đặt tại nút 1 đến
nơi xảy ra hoả hoạn ở nút n, trong thời gian ít nhất có thể. Thời gian và chi phí năng lượng để
robot đi trên đường nối trực tiếp từ nút i đến nút j tương ứng là tij và cij (1 <= i, j <= n). Robot
chỉ có thể đi được trên đường nối trực tiếp từ nút i đến nút j nếu năng lượng còn lại trong bình
chứa không ít hơn cij (1 <= i, j <= n). Nếu robot đi đến một nút có trạm tiếp năng lượng (một
nút có thể có hoặc không có trạm tiếp năng lượng) thì nó tự động được nạp đầy năng lượng
vào bình chứa với thời gian nạp coi như không đáng kể.
Yêu cầu: Hãy xác định giá trị w nhỏ nhất để robot đi được trên một đường đi từ nút 1 đến nút
n trong thời gian ít nhất.
Input: Đọc từ file QBROBOT.INP:
Dòng đầu tiên chứa một số nguyên dương n (2 <= n <= 500);

13
Dòng thứ hai chứa n số, trong đó số thứ j bằng 1 hoặc 0 tương ứng ở nút j có hoặc
không có trạm tiếp năng lượng (j = 1, 2, …, n);
Dòng thứ ba chứa số nguyên dương m (m <= 30000) là số đường nối trực tiếp có trong
mạng lưới giao thông;
Dòng thứ k trong số m dòng tiếp theo chứa 4 số nguyên dương i, j, t ij, cij (tij, cij <=
10000) mô tả đường nối trực tiếp từ nút i đến nút j, thời gian và chi phí năng lượng
tương ứng.
Hai số liên tiếp trên một dòng trong file dữ liệu cách nhau ít nhất một dấu cách.
Output: Ghi ra file QBROBOT.OUT: Ghi ra số nguyên dương w tìm được.
Ví dụ:
QBROBOT.INP QBROBOT.OUT
4 3
0110
5
1254
1343
1494
2441
3452
* Hướng dẫn giải:
Trước hết ta nhận thấy rằng thời gian đi nhỏ nhất từ 1 -> N được ưu tiên hàng đầu. Vì vậy
dùng Dijkstra để tìm đường đi ngắn nhất từ 1 -> N, gọi d[i] là khoảng cách nhỏ nhất từ i ->
N. Sau đó sẽ tìm giá trị w nhỏ nhất mà có thể đi với thời gian ngắn nhất vừa tìm được là d[1].
Bài toán có thể giải quyết bằng phương pháp tìm kiếm nhị phân như sau:
Vì w chắc chắn nằm trong đoạn từ 1 -> +  nên: d := 1 và c := 100000000;
Lặp khi d <= c
- w := (d + c)/2
- Thực hiện tìm kiếm theo chiều sâu bắt đầu từ đỉnh 1 tìm đến đỉnh N:
o Nếu không tìm được đường đi từ 1 -> N với thời gian ngắn nhất (nghĩa là w hơi
bé) cần tăng giá trị w lên bằng cách chặt nhị phân tìm w từ d := w + 1 -> c
o Ngược lại: w đã thoản mãn có thể đi từ 1 -> N với thời gian ngắn nhất. Tuy
nhiên w chưa chắc là nhỏ nhất. Vì vậy cần giảm giá trị w bằng cách chặt nhị
phân trong đoạn từ d -> c = w – 1
- Vấn đề cần giải quyết bây giờ là trong thủ tục DFS(1) thì điều kiện để đi từ đỉnh u ->
v là gì?
14
- Câu hỏi trên sẽ được giải đáp nếu trả lời được 3 câu hỏi sau:
o Từ u -> v có đường đi trực tiếp không? nếu có đường đi trực tiếp thì trả lời câu
hỏi tiếp.
o Đỉnh u có trạm tiếp năng lượng không? hoặc năng lượng còn lại của robot có
thể đi từ u -> v không. Nếu thỏa mãn thì trả lời câu hỏi tiếp:
o Đường đi từ u -> v có đảm bảo là nằm trên một đường đi từ 1 -> N với thời gian
ngắn nhất không? tức là tổng thời gian từ 1 –> u với t(u,v) và d[v] có bằng d[1]
không?

15

You might also like