Tải bản đầy đủ (.docx) (4 trang)

Chặt nhị phân theo kết quả

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 (89.48 KB, 4 trang )

1. Chặt nhị phân theo kết quả a.
Xét bài toán
Cho n đoạn dây điện (1 ≤ n ≤ 105 ). Đoạn dây thứ i có độ dài ai (0 < ai ≤ 109 ). Cần phải cắt
các đoạn đã cho thành các đoạn sao cho có được K đoạn dây bằng nhau có độ dài nguyên. Có
thể không cần cắt hết các đoạn dây đã cho. Mỗi đoạn dây bị cắt có thể có phần còn thừa khác
0.
Yêu cầu: Xác định độ dài lớn nhất của đoạn dây có thể nhận được. Nếu không có cách cắt thì
đưa ra số 0.
Dữ liệu: file văn bản WIRES.INP có cấu trúc Dòng đầu tiên chứa hai số nguyên N, K Dòng
thứ i trong N dòng tiếp theo chứa số nguyên ai
Kết quả: Đưa ra file văn bản WIRES.OUT, Một dòng duy nhất ghi độ dài lớn nhất có thể
nhận được.
Wires.inp Wires.out
4 11
200
802
743
547
539
Giải quyết bài toán
Ta dùng một mảng A lưu độ dài các đoạn dây, biến Res lưu kết quả bài toán, biến sum là tổng
độ dài của N đoạn dây. Bài toán trên có thể được giải bằng giải thuật như sau:
ta thử lần lượt độ dài x (x nhận giá trị từ 1 tới (sum div k)), sau đó kiểm tra xem có cắt được
K đoạn có độ dài x không?. Hàm kiểm tra xem có cắt được K đoạn có độ dài x như sau:
Function Check(x: Longint): Boolean;
Var i,count:longint;
Begin
Count:=0;
For i:=1 to n do Count:= Count + A[i] div x;
Check:=(Count>=K);
End;


Đoạn chương trình xét lần lượt các giá trị của x như sau:
Res:=0;// Res là biến lưu kết quả bài toán
For x:=1 to (sum div k) do If Check(x) then Res:=x;
Ta thấy hàm Check(x) có độ phức tạp O(n), như vậy đoạn chương trình xét từng giá trị của x
sẽ có độ phức tạp O(nL) với L=min(sum div k, max(ai) ). Theo đề bài thì n ≤ 105 và ai ≤ 109 , do đó
giải thuật trên chỉ giải quyết được bài toán với những bộ dữ liệu nhỏ. Để giải quyết trọn vẹn bài toán


ta cần giảm độ phức tạp thuật toán. Chi phí của hàm Check(x) phụ thuộc vào N, ta chỉ có thể giảm số
phép thử của x như sau:
Giả sử phạm vi giá trị là [dau .. cuoi], xét một giá trị x:=(dau+cuoi) div 2, nếu kết quả bài
toán là x hoặc nằm trong đoạn [x+1 .. cuoi],Check(x) = true ngược lại kết quả bài toán nằm trong
đoạn [dau .. x-1], quá trình nàylặp đi lặp lại cho tới khi dau>=cuoi thì dừng.
Đoạn chương trình xét lần lượt các giá trị của x viết lại như sau:
Res:=0; Dau:=0; cuoi:=sum div k+1;
While ( dau< cuoi) do
Begin
X:= (dau+cuoi) div 2;
If Check(x) then
Begin
Res:=x;//ghi nhận kết quả sau mỗi lần Check(x)=true
Dau:=x+1;
End
Else cuoi:=x-1;
End;
Đoạn chương trình này chi phí xét các giá trị của x chỉ còn:O(log(cuoi-dau+1) ), vậy độ phức
tạp của thuật toán là O(nlogL) đáp ứng được yêu cầu bài toán. Tư tưởng giảm chi phí xét các khả
năng của x trong thuật toán trên hoàn toàn giống với quá trình tìm khóa trong thuật toán tìm kiếm nhị
phân. Các bài toán áp dụng kỹ thuật chặt nhị phân theo kết quả thường có yêu cầu là tìm giá trị nhỏ
nhất hoặc lớn nhất. Tùy vào từng yêu cầu mà ta có cấu trúc đoạn chương trình chặt nhị phân khác

nhau.
Trường hợp tìm giá trị lớn nhất:
Dau:=a-1; cuoi:=b+1;
While ( dauBegin
giua:= (dau+cuoi) div 2;
If Check(giua) then
Begin
Res:=giua;//ghi nhận kết quả sau mỗi lần Check()=true


Dau:=giua+1;
End Else cuoi:=giua-1;
End;
Trường hợp tìm giá trị nhỏ nhất:
Dau:=a-1; cuoi:=b+1;
While ( dauBegin
giua:= (dau+cuoi) div 2;
If Check(giua) then
Begin
Res:=giua;//ghi nhận kết quả sau mỗi lần Check()=true
cuoi:=giua-1;
End
Else Dau:=giua+1;

End;
Cho một dãy a đã sắp xếp tăng dần. Lần lượt nhập m số k. Có hai bài toán:
a, Với mỗi k, in ra số đầu tiên bé nhất lơn hơn hoặc bằng k.
b, Với mỗi k, in ra số cuối cùng lớn nhất bé hơn hoặc bằng k.

Tức là ở bài toán a, trong các a[i] thỏa mãn và bằng nhau, chọn a[i] có i nhỏ nhất. Ở bài toán b, trong các a[i]
thỏa mãn và bằng nhau, chọn a[i] có i lớn nhất.
Trở lại với cách làm ở trên, đây là nội dung mã giả ở trên
sort(a);
left=1, right=n;
i = (left + right) / 2;
while (left != i) and (i != right) do
if a[i]>=k then right = i;
else left = i;
i = (left + right) / 2;
for i = left to right do
if a[i]>=k then return a[i];
print "Not found"
Để ý rằng dòng tô đỏ (if a[i]>k ...) ở trên, sẽ gán right = i cả khi a[i] = k. Vì thế kết quả luôn là a[i] với i nhỏ
nhất. Để chọn i lớn nhất, ta thay đổi hai dòng tô đỏ ở trên thành:
sort(a);
left=1, right=n;
i = (left + right) / 2;


while (left != i) and (i != right) do
if a[i]>k then right = i;
else left = i;
i = (left + right) / 2;
for i = right to left do
if a[i]<=k then return a[i];
print "Not found"




×