check(MAX): Với sức chở MAX, chuyển được trong D ngày không?
- MAX tăng → số ngày giảm → đơn điệu!
- Binary search trên MAX từ max(weights) đến sum(weights)
boolcheck(vector<int>&piles,intspeed,inth){longlonghours=0;for(intp:piles)hours+=(p+speed-1)/speed;// Làm tròn lênreturnhours<=h;}intminEatingSpeed(vector<int>&piles,inth){intlo=1,hi=*max_element(piles.begin(),piles.end());while(lo<hi){intmid=lo+(hi-lo)/2;if(check(piles,mid,h))hi=mid;elselo=mid+1;}returnlo;}
// SAI: Có thể lặp vô hạn khi tìm max mà check(mid) = truewhile(lo<hi){intmid=lo+(hi-lo)/2;// mid luôn = lo khi hi = lo + 1if(check(mid))lo=mid;// lo không đổi → lặp vô hạn!elsehi=mid-1;}// ĐÚNG: Dùng mid = lo + (hi - lo + 1) / 2 khi tìm maxwhile(lo<hi){intmid=lo+(hi-lo+1)/2;// Làm tròn lênif(check(mid))lo=mid;elsehi=mid-1;}
Quy tắc: Tìm min → hi = mid, Tìm max → lo = mid → cần +1 để tránh lặp vô hạn.
// SAI: Cận dưới quá nhỏ hoặc cận trên quá lớn → TLE hoặc saiintlo=0,hi=1e18;// Quá rộng → nhiều bước hơn// ĐÚNG: Chọn cận chặt// Cận dưới: giá trị nhỏ nhất có thể là kết quả// Cận trên: giá trị lớn nhất có thể là kết quả// Ví dụ: Capacity To Shipintlo=*max_element(weights.begin(),weights.end());// Gói nặng nhấtinthi=accumulate(weights.begin(),weights.end(),0);// Tổng tất cả
// SAI: Dùng int khi kết quả có thể là số thựcintmid=lo+(hi-lo)/2;// Mất phần thập phân!// ĐÚNG: Dùng double khi cần chính xácdoublemid=lo+(hi-lo)/2.0;// Hoặc nhân lên để dùng int (tránh số thực)// Ví dụ: Cần chính xác 1e-6 → nhân 1e6, dùng int, chia kết quả cuối
// SAI: check không có tính đơn điệu → binary search không áp dụng đượcboolcheck(intx){returnx*x-5*x+6<=0;// Parabol → không đơn điệu!}// ĐÚNG: Chỉ dùng binary search khi check có tính chất:// Nếu check(x) = true thì mọi y > x cũng = true (hoặc ngược lại)
// SAI: Trả về lo mà không kiểm trareturnlo;// Có thể lo không thỏa mãn nếu không có nghiệm!// ĐÚNG: Kiểm tra sau khi binary searchif(check(lo))returnlo;return-1;// Không có nghiệm