thuattoan.dev

Level 1 · Cơ bản

Tìm kiếm nhị phân

Binary search trên mảng đã sắp xếp và trên đáp án.

Độ khó: Trung bình Visualizer tương tác7 bài luyện tập

Ý tưởng chính

Binary search giảm không gian tìm kiếm đi một nửa sau mỗi bước, đạt độ phức tạp O(log n). Áp dụng được cả khi tìm kiếm trên miền giá trị đáp án.

Hình dung

Trò đoán số từ 1 đến 100: mỗi lần bạn đoán số ở giữa, người kia chỉ nói "lớn hơn" hay "nhỏ hơn". Chỉ cần tối đa 7 lần đoán là ra, vì mỗi lần loại được một nửa khả năng.

Cách hoạt động

  1. 1

    Giữ một đoạn [lo, hi] chắc chắn chứa đáp án (nếu có).

  2. 2

    Xét phần tử giữa mid = (lo + hi) / 2.

  3. 3

    Nếu arr[mid] bằng giá trị cần tìm thì xong; nếu nhỏ hơn thì đáp án nằm bên phải (lo = mid + 1); nếu lớn hơn thì nằm bên trái (hi = mid − 1).

  4. 4

    Lặp lại đến khi lo > hi (không tìm thấy).

Độ phức tạp

O(log n): mảng 1 triệu phần tử chỉ cần khoảng 20 lần so sánh.

Khi nào dùng

Mảng đã sắp xếp, hoặc bất kỳ bài nào có tính đơn điệu: "nếu x thoả thì mọi giá trị lớn hơn x cũng thoả" — khi đó có thể tìm kiếm trên đáp án.

Lỗi hay gặp

  • Áp dụng lên mảng chưa sắp xếp — kết quả sai mà không báo lỗi.
  • Tràn số khi tính (lo + hi) với số lớn; dùng lo + (hi − lo) / 2.
  • Lặp vô hạn do cập nhật lo = mid thay vì mid + 1.

Liên hệ thực tế

Ứng dụng thực tế

Kỹ thuật này xuất hiện ở đâu ngoài đời — để bạn có sẵn một hình ảnh cụ thể khi học.

  • Tra từ điển, danh bạ giấy

    Mở ở giữa, thấy từ cần tìm đứng trước hay sau rồi bỏ đi một nửa — mỗi lần lật loại bỏ phân nửa số trang.

  • git bisect tìm commit gây lỗi

    Với 1.000 commit, Git chỉ cần bạn kiểm tra khoảng 10 lần để chỉ ra chính xác commit làm hỏng chương trình.

  • Chỉ mục trong cơ sở dữ liệu

    Tìm bản ghi theo khoá trên dữ liệu đã sắp xếp (B-tree, file index) mà không phải quét toàn bộ bảng.

  • Tìm cấu hình tối thiểu

    Tìm kiếm trên đáp án: cần ít nhất bao nhiêu máy chủ để chịu tải? Thử một con số, đủ thì giảm, thiếu thì tăng.

Xem thuật toán chạy

Minh hoạ từng bước

Chạy tự động hoặc đi từng bước; theo dõi hình minh hoạ, lời giải thích, giá trị biến và dòng code tương ứng cùng lúc.

Tự làm

Luyện tập trên Codeforces

Sắp xếp từ dễ đến khó theo rating, mỗi bài kèm tóm tắt đề. Bí thì mở gợi ý — có 3 mức, từ hướng suy nghĩ tới gần lời giải.

  • Hội trường có n hàng, mỗi hàng m ghế; cần xếp k thí sinh. Một 'băng ghế' là dãy các ghế có người liền nhau trong một hàng. Tìm độ dài lớn nhất nhỏ nhất có thể của một băng ghế.

    Test mẫu & giải thích
    Input
    5
    3 4 7
    5 5 5
    1 13 2
    2 4 7
    1 5 4
    Output
    2
    1
    1
    4
    2

    Với test đầu n=3,m=4,k=7: nếu độ dài băng ghế tối đa cho phép là L thì mỗi hàng m ghế xếp được tối đa m - floor(m/(L+1)) người (xen kẽ khối L người liền nhau với 1 ghế trống để cắt mạch). Với L=1, mỗi hàng chỉ xếp được 4-2=2 người, 3 hàng tối đa 6 người < 7 nên không đủ chỗ. Với L=2, mỗi hàng xếp được 4-1=3 người, 3 hàng đủ 9 ≥ 7 chỗ, nên L=2 là giá trị nhỏ nhất khả thi, khớp kết quả in ra là 2.

  • Có n lá bài xếp chồng, lá i có số a_i. Mỗi vòng lấy một số lá (ít nhất 1) từ trên cùng; vòng thắng nếu tổng nằm trong [l, r]. Chơi đến hết bài — tìm số vòng thắng lớn nhất.

    Test mẫu & giải thích
    Input
    8
    5 3 10
    2 1 11 3 7
    10 1 5
    17 8 12 11 7 11 21 13 10 8
    3 4 5
    3 4 2
    8 12 25
    10 7 5 13 8 9 12 7
    2 3 3
    5 2
    9 7 9
    2 10 5 1 3 7 6 2 3
    1 8 10
    9
    5 5 6
    1 4 2 6 4
    Output
    3
    0
    1
    4
    0
    3
    1
    2

    Với test đầu (n=5,l=3,r=10,a=[2,1,11,3,7]): nên lấy số lá ÍT NHẤT để tổng vừa đạt ≥ l, vì lấy dư sẽ tốn lá cho các vòng sau. Vòng 1 lấy 2 lá đầu (2+1=3), tổng vừa đạt l=3 và ≤ r=10 nên thắng, con trỏ chuyển tới lá 11. Một lá 11 đã vượt r=10 nên chắc chắn thua, buộc bốc đúng 1 lá này để giữ tối đa lá còn lại. Hai lá cuối 3 và 7: lấy riêng lá 3 (tổng 3, trong [3,10]) là một vòng thắng, rồi lấy nốt lá 7 (tổng 7, trong [3,10]) cũng thắng. Tổng cộng 3 vòng thắng và 1 vòng thua, khớp đáp số 3.

  • Có n mô hình, mô hình i giá i đồng, bán từ ngày i đến ngày n; chuỗi s cho biết ngày nào Monocarp đến cửa hàng. Nếu mua ≥ 2 mô hình trong một ngày thì mô hình đắt nhất hôm đó miễn phí. Tìm chi phí nhỏ nhất để mua mỗi mô hình đúng một lần.

    Test mẫu & giải thích
    Input
    4
    1
    1
    6
    101101
    7
    1110001
    5
    11111
    Output
    1
    8
    18
    6
    1. Test 1

      Với test n=1, s='1': chỉ có một mô hình giá 1 và một ngày ghé duy nhất nên buộc mua với giá gốc 1, khớp kết quả 1.

    2. Test 2

      Với test n=6, s='101101' (các ngày ghé là 1,3,4,6): mô hình 5 và 6 chỉ được bán từ ngày 5,6 nên bắt buộc gộp vào ngày 6, mua cùng lúc khiến mô hình 6 (đắt nhất) được miễn phí, chỉ tốn 5 đồng. Mô hình 4 phải mua ở ngày 4, ghép với mô hình 1 (rẻ nhất còn lại) để mô hình 4 được miễn phí, tốn 1 đồng. Mô hình 2 và 3 mua ở ngày 3, ghép đôi để mô hình 3 (đắt hơn) miễn phí, tốn 2 đồng. Tổng chi phí 5+1+2=8, khớp kết quả.

  • Xe đi trên trục số từ 0 đến n, qua các biển báo a_1 < … < a_k = n tại các thời điểm b_1 < … < b_k; giữa hai biển xe đi đều. Với mỗi truy vấn d, in thời điểm xe tới d (làm tròn xuống).

    Test mẫu & giải thích
    Input
    4
    10 1 3
    10
    10
    0
    6
    7
    10 2 4
    4 10
    4 7
    6
    4
    2
    7
    1000000000 1 1
    1000000000
    1000000000
    99999999
    6 1 3
    6
    5
    2
    6
    5
    Output
    0 6 7 
    5 4 2 5 
    99999999 
    1 5 4 

    Với test đầu (n=10, chỉ một biển báo tại vị trí 10 ở thời điểm 10, xuất phát ngầm định từ vị trí 0 tại thời điểm 0): xe đi đều trên đoạn 0→10 trong 10 đơn vị thời gian nên tốc độ là 1, tức thời điểm tới vị trí d chính bằng d. Với d=0 xe đang ở điểm xuất phát nên thời điểm là 0; d=6 xe tới lúc 6; d=7 xe tới lúc 7, khớp đúng dãy in ra '0 6 7'.

  • Cho cây gốc 1 với giá trị a_i. Mỗi thao tác chọn đỉnh v có con: tăng a_v thêm 1 và giảm mọi đỉnh khác trong cây con của v đi 1; mọi giá trị phải luôn không âm. Tìm giá trị lớn nhất có thể ở gốc.

    Test mẫu & giải thích
    Input
    3
    4
    0 1 0 2
    1 1 3
    2
    3 0
    1
    5
    2 5 3 9 6
    3 1 5 2
    Output
    1
    3
    6

    Với test đầu (cây gốc 1 có con 2,3; đỉnh 3 có con 4; a=[0,1,0,2]): không thể tác động ngay tại gốc vì đỉnh 3 đang bằng 0, giảm thêm sẽ âm. Thực hiện thao tác tại đỉnh 3 trước: a_3 tăng lên 1, a_4 (con duy nhất trong cây con của 3) giảm xuống 1, mảng thành [0,1,1,1]. Lúc này thực hiện thao tác tại gốc: a_1 tăng lên 1, còn a_2,a_3,a_4 đều giảm về 0 (vẫn hợp lệ vì không âm). Không thể tăng thêm nữa vì mọi đỉnh con đều đã bằng 0, nên giá trị gốc lớn nhất đạt được là 1, khớp kết quả.

  • Có n thành phố trên một đường thẳng, mỗi thành phố có cổng dịch chuyển của 2 trong 4 màu (B, G, R, Y). Đi giữa hai thành phố chung một màu tốn |i − j|. Trả lời q truy vấn: chi phí nhỏ nhất từ x tới y, hoặc −1.

    Test mẫu & giải thích
    Input
    2
    4 5
    BR BR GY GR
    1 2
    3 1
    4 4
    1 4
    4 2
    2 1
    BG RY
    1 2
    Output
    1
    4
    0
    3
    2
    -1
    1. Test 1

      Với test đầu, thành phố 1 và 2 đều có cổng BR nên đi thẳng 1→2 tốn |1-2|=1, khớp giá trị đầu tiên.

    2. Test 2

      Với truy vấn (3,1): thành phố 3 là GY và thành phố 1 là BR không chung màu nào nên phải trung chuyển; thành phố 4 là GR chung màu G với thành phố 3 và chung màu R với thành phố 1, cho quãng đường 3→4→1 với chi phí |3-4|+|4-1|=1+3=4, khớp giá trị thứ hai.

    3. Test 3

      Ở test thứ hai chỉ có 2 thành phố với màu BG và RY hoàn toàn rời nhau, không có thành phố trung gian nào để nối nên không thể di chuyển được, trả về -1.

  • Cho hoán vị p và số x. Thuật toán binary search cố định (l = 1, r = n + 1; khi r − l > 1: m = (l + r)/2, nếu p_m ≤ x thì l = m, ngược lại r = m) chạy trên p dù p chưa sắp xếp. Dùng tối đa 2 phép đổi chỗ để kết thúc có p_l = x; in các phép đổi.

    Test mẫu & giải thích
    Input
    5
    6 3
    1 2 3 4 5 6
    6 5
    3 1 6 5 2 4
    5 1
    3 5 4 2 1
    6 3
    4 3 1 5 2 6
    3 2
    3 2 1
    Output
    0
    1
    3 4
    2
    2 4
    1 5
    2
    4 5
    2 4
    1
    1 3
    1. Test 1

      Ở test đầu p đã là hoán vị tăng dần 1..6 với x=3: chạy thuật toán nhị phân chuẩn, l hội tụ đúng về vị trí 3 (nơi chứa giá trị 3) nên không cần đổi chỗ nào, in ra 0.

    2. Test 2

      Ở test thứ hai, p=[3,1,6,5,2,4], x=5: chạy thuật toán gốc sẽ hội tụ sai về vị trí 6 (giá trị 4). Đổi chỗ vị trí 3 và 4 (giá trị 6 và 5) biến mảng thành [3,1,5,6,2,4]; chạy lại thuật toán: m=4 có p_4=6>5 nên r=4, m=2 có p_2=1≤5 nên l=2, m=3 có p_3=5≤5 nên l=3, dừng ở l=3 với p_3=5=x, đúng yêu cầu chỉ bằng 1 phép đổi chỗ (3,4), khớp kết quả.