Khoá học C++ nâng cao
Bài học: Đệ Quy, Chia Để Trị & Kỹ Thuật Meet in the Middle
🏆 +100 XP tiềm năng Thoát

Đệ Quy, Chia Để Trị & Kỹ Thuật Meet in the Middle

2 khối nội dung 45 phút học tập MỞ BẢNG TRẮNG

Đệ Quy, Chia Để Trị & Kỹ Thuật Meet in the Middle

Nội dung bài học

1. Khái niệm & bản chất của phân rã không gian tìm kiếm

Đệ quy (Recursion) và Chia để trị (Divide and Conquer) là nền tảng tư duy cốt lõi trong khoa học máy tính: chia bài toán lớn thành các bài toán con đồng dạng có kích thước nhỏ hơn, giải quyết độc lập và kết hợp nghiệm.

Ở Level 2, ta khai thác bước nhảy vọt về tư duy tối ưu hóa:

  • Cây đệ quy & Định lý thợ (Master Theorem): Phân tích chính xác chi phí thời gian của các hàm đệ quy phân nhánh $T(N) = a T(N/b) + \mathcal{O}(N^d)$.
  • Kỹ thuật Đếm nghịch thế (Inversion Count): Vận dụng Merge Sort để đếm số cặp nghịch thế $i < j$ mà $A_i > A_j$ trong $\mathcal{O}(N \log N)$ (thay vì duyệt ngây thơ $\mathcal{O}(N^2)$).
  • Kỹ thuật Gặp nhau ở giữa (Meet in the Middle - MITM): Khi không gian tìm kiếm là $2^N$ với $N = 40$ ($2^{40} \approx 10^{12} \implies \text{TLE}$), ta chia đôi tập hợp thành hai nửa $N/2 = 20$. Duyệt hai nửa độc lập ($2 \times 2^{20} \approx 2 \times 10^6$) rồi dùng Two Pointers / Binary Search để ghép nghiệm $\implies$ Giảm độ phức tạp từ $\mathcal{O}(2^N)$ xuống $\mathcal{O}(2^{N/2} \log(2^{N/2}))$.

2. Kỹ thuật đếm số cặp nghịch thế bằng Merge Sort

2.1. Bản chất toán học

Trong quá trình trộn (merge) hai nửa đã sắp xếp $[L \dots mid]$ và $[mid+1 \dots R]$: Nếu phần tử bên nửa phải $A[j]$ nhỏ hơn phần tử bên nửa trái $A[i]$ ($A[j] < A[i]$), thì do nửa trái đã tăng dần, $A[j]$ sẽ nhỏ hơn tất cả các phần tử từ $i$ đến $mid$.
Số lượng cặp nghịch thế tạo bởi $A[j]$ chính là: $$\Delta = mid - i + 1$$

long long merge_and_count(vector<int> &a, int l, int mid, int r) {
    vector<int> left(a.begin() + l, a.begin() + mid + 1);
    vector<int> right(a.begin() + mid + 1, a.begin() + r + 1);
    int i = 0, j = 0, k = l;
    long long inv_count = 0;

    while (i < left.size() && j < right.size()) {
        if (left[i] <= right[j]) {
            a[k++] = left[i++];
        } else {
            a[k++] = right[j++];
            inv_count += (left.size() - i); // Khai thác tính chất tăng dần
        }
    }
    while (i < left.size()) a[k++] = left[i++];
    while (j < right.size()) a[k++] = right[j++];
    return inv_count;
}

Kỹ thuật Meet in the Middle

3. Kỹ thuật Meet in the Middle

3.1. Bài toán Knapsack với $N \le 40$ và $W \le 10^{18}$

  • Không thể dùng Quy hoạch động vì $W = 10^{18}$ quá lớn.
  • Không thể duyệt nhánh cận toàn phần vì $2^{40} \approx 10^{12}$ quá lớn.

3.2. Thuật toán 3 bước MITM

  1. Nửa 1 ($N_1 = 20$): Sinh tất cả $2^{20}$ tổng tập con, lưu vào vector<long long> sum1. Sắp xếp và lọc bỏ các trạng thái không tối ưu.
  2. Nửa 2 ($N_2 = 20$): Sinh tất cả $2^{20}$ tổng tập con, lưu vào vector<long long> sum2.
  3. Ghép nghiệm: Với mỗi giá trị $S \in sum2$, tìm giá trị lớn nhất trong $sum1$ mà $\le W - S$ bằng upper_bound trong $\mathcal{O}(\log(2^{N_1}))$.

4. Mẫu cài đặt chuẩn thi đấu: Meet in the Middle

#include <bits/stdc++.h>
using namespace std;

void generate_sums(int idx, int end_idx, long long current_sum, const vector<long long> &a, vector<long long> &res) {
    if (idx == end_idx) {
        res.push_back(current_sum);
        return;
    }
    generate_sums(idx + 1, end_idx, current_sum, a, res);            // Không chọn
    generate_sums(idx + 1, end_idx, current_sum + a[idx], a, res);    // Có chọn
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    long long w;
    if (!(cin >> n >> w)) return 0;

    vector<long long> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i];

    int mid = n / 2;
    vector<long long> sum1, sum2;
    generate_sums(0, mid, 0, a, sum1);
    generate_sums(mid, n, 0, a, sum2);

    sort(sum2.begin(), sum2.end());

    long long max_weight = 0;
    for (long long s1 : sum1) {
        if (s1 <= w) {
            auto it = upper_bound(sum2.begin(), sum2.end(), w - s1);
            if (it != sum2.begin()) {
                --it;
                max_weight = max(max_weight, s1 + *it);
            }
        }
    }

    cout << max_weight << "\n";
    return 0;
}

5. Ranh giới áp dụng

Phạm Vi $N$ Thuật Toán Tối Ưu Độ Phức Tạp
$N \le 20$ Duyệt đệ quy / Bitmask toàn phần $\mathcal{O}(2^N)$
$N \le 40$ Meet in the Middle (MITM) $\mathcal{O}(2^{N/2} \log(2^{N/2}))$
$N \le 10^5, W \le 10^5$ Quy hoạch động Cái túi (DP Knapsack) $\mathcal{O}(NW)$
$N \le 10^5, W \le 10^{18}$ Tham lam (nếu các phần tử chia hết) $\mathcal{O}(N \log N)$

1
A
B
C
D
2
A
B
C
D

Bài tập lập trình vận dụng

22 Bài tập thực hành

Nhấn vào nút "Làm bài" bên dưới để mở giao diện làm bài trực tuyến (Online Judge) và thực hiện viết mã nguồn cho bài tập này.

Đếm cặp nghịch thế
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Cái túi kích thước nhỏ (knapsack $n \le 40$)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tập con có tổng gần s nhất
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Giải phương trình $4$ ẩn tuyến tính (4-sum mitm)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số tập con có xor bằng k
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Khoảng cách giữa hai điểm gần nhất (closest pair)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Bẻ khóa mật mã đổi dấu (subset sum with signs)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tối ưu hóa tuyến đường đi qua đỉnh (shortest path with mitm)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Trò chơi xếp gạch đa diện (puzzle mitm)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm cặp $a_i > 2 a_j$ (significant inversions)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng cấp số nhân bằng chia để trị
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tối ưu hóa tuyến đường đi qua đỉnh (shortest path mitm)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Trò chơi xếp gạch đa diện (15-puzzle mitm)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Phân chia tập hợp thành hai nửa có tổng bằng nhau
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số đoạn con có tổng nằm trong $[l, r]$
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Chia để trị trên cây (centroid decomposition cơ bản)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Centroid decomposition co ban
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Dem chu trinh 4 canh mitm
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Chia de tri day con tong max
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Mitm dem nghiem nguyen tong bang 0
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tim cap diem gan nhat 2d
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Dem nghich the 3 chieu cdq
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài