Tìm kiếm nhị phân cho bài toán "cưa gỗ"
Bài toán Cưa gỗ
Trong khu vườn, người ta trồng một hàng cây chạy dài gồm có N cây, mỗi cây có độ cao là a1, a2,…aN.
Người ta cần lấy M mét gỗ bằng cách đặt cưa máy sao cho lưỡi cưa ở độ cao H (mét) để cưa tất cả các cây có độ cao lớn hơn H (dĩ nhiên những cây có độ cao không lớn hơn H thì không bị cưa).
Ví dụ: Nếu hàng cây có các cây với độ cao tương ứng là 20; 15; 10 và 18 mét, cần lấy 7 mét gỗ. Lưỡi cưa đặt tại độ cao hợp lí là 15 mét thì độ cao của các cây còn lại sau khi bị cưa tương ứng là 15; 15; 10 và 15 mét. Tổng số mét gỗ lấy được là 8 mét (dư 1 mét).
Yêu cầu: Hãy tìm vị trí đặt lưỡi cưa hợp lí (số nguyên H lớn nhất) sao cho lấy được M mét gỗ và số mét gỗ dư ra là ít nhất.
Dữ liệu vào:Đọc từ file văn bản WOOD.INP có cấu trúc
- Dòng thứ nhất chứa 2 số nguyên dương N và M (1≤N≤106; 1≤M≤2.109) cách nhau một dấu cách.
- Dòng thứ hai chứa N số nguyên dương ai là độ cao của mỗi cây trong hàng (1≤ai≤109; i=1…N), mỗi số cách nhau ít nhất một dấu cách.
Kết quả: Ghi ra file WOOD.OUT, một dòng duy nhất ghi một số nguyên cho biết giá trị cần tìm.
Ví dụ:
|
WOOD.INP |
WOOD.OUT |
|
4 7 20 15 10 18 |
15 |
Hướng dẫn:
Gọi Max là giá trị lớn nhất của hàng cây.
Cách 1:
Duyệt vị trí đặt lưỡi cưa từ Max về 0, Nếu thỏa mãn thì lấy vị trí và kết thúc.
Function IsOk(h:Longint):Boolean;
Var i:Longint;
Begin
S:=0;
For i:=1 To N Do
If A[i]>h Then S:=S+A[i]-h;
Exit(S>=M);
End;
Function Wood:Longint;
Begin
For h:=Max Downto 0 Do
If IsOK(h) Then Exit(h);
End;
Độ phức tạp là O(N.Max).
Cách 2:
Tìm kiếm nhị phân theo độ cao cần đặt lưỡi cưa như sau:
d=0, c= Max,
Lặp khi d ≤ c.
h=(d+c) Div 2
Kiểm tra số mét gỗ được lấy ra khi chặt ở độ cao h có thỏa không?
- Nếu thỏa Thì tìm kiếm nhị phân h từ d=h đến c.
- Ngược lại thì tìm kiếm nhị phân h từ d đến c=h-1
Function Wood:Longint;
Begin
d:=0;c:=Max;
While d ≤ c Do
Begin
h:=(d+c) Div 2;
If IsOK(h) Then d:=h
Else c:=h-1;
End;
Exit(h);
End;
Độ phức tạp là O(N.Log(Max)).