Chia để trị sâu hơn
Chia để trị (divide and conquer) là tư duy "việc lớn thì bẻ đôi". Ta đã gặp nó ở bài 1.3.4; giờ ta đi sâu hơn với ba bài toán đẹp: đếm nghịch thế, tìm dãy con tổng lớn nhất, và lũy thừa nhanh. Bạn sẽ thấy cùng một mẫu ba bước giải được những bài tưởng chừng rất khác nhau.
1. Trực quan: chia đôi mãi cho tới khi dễ
Ôn lại tinh thần: gặp bài lớn, chia thành vài bài con nhỏ hơn (thường là hai nửa), giải từng phần (thường bằng đệ quy), rồi gộp các lời giải con thành lời giải lớn.
[ bai lon ]
╱ ╲ CHIA
[nua trai] [nua phai]
╱ ╲ ╱ ╲ ... chia tiep
... ... ... ...
╲ ╱ ╲ ╱ GOP nguoc len
[giai trai] [giai phai]
╲ ╱
[ ket qua ] GHEP lai
Ba bước vàng: Chia (divide) → Giải (conquer) → Gộp (combine). Phần "thông minh" của mỗi thuật toán nằm ở bước gộp — đó là nơi quyết định bài có nhanh hay không.
2. Mẫu chung và liên hệ merge sort
Khung chia để trị:
def giai(bai):
if bai du nho: # truong hop co so
return giai_truc_tiep(bai)
trai, phai = chia_doi(bai) # CHIA
a = giai(trai) # GIAI (de quy)
b = giai(phai) # GIAI
return gop(a, b) # GOP
Bạn đã thấy mẫu này ở merge sort (/thuat-toan/merge-sort): chia mảng
làm đôi, sắp xếp mỗi nửa, rồi trộn (merge) hai nửa đã sắp. Bước gộp O(n), đệ quy log n tầng →
tổng O(n log n). Ba ví dụ dưới đây dùng đúng tinh thần đó.
3. Ví dụ 1: Đếm nghịch thế (inversions)
Nghịch thế là cặp (i, j) với i < j nhưng a[i] > a[j] — một cặp "đứng sai thứ tự". Số nghịch
thế đo độ "lộn xộn" của mảng. Cách ngây thơ duyệt mọi cặp tốn O(n^2); chia để trị làm trong
O(n log n) bằng cách đếm khi trộn (như merge sort).