Khoá học C++ nâng cao
Bài học: Thuật Toán Tham Lam (Greedy)
🏆 +100 XP tiềm năng Thoát

Thuật Toán Tham Lam (Greedy)

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

Thuật Toán Tham Lam (Greedy)

Nội dung bài học

1. Khái niệm & bản chất của lựa chọn tối ưu cục bộ

Thuật toán Tham lam (Greedy Algorithm) là chiến lược giải quyết bài toán tối ưu bằng cách thực hiện một chuỗi các lựa chọn tối ưu cục bộ (locally optimal choice) ở từng bước, với hy vọng dẫn đến nghiệm tối ưu toàn cục (globally optimal solution) mà không cần phải quay lui (backtracking) hay tính toán lại các trạng thái trước đó.

Để một bài toán giải được bằng thuật toán tham lam, nó bắt buộc phải thỏa mãn 2 điều kiện toán học khắt khe:

  1. Tính chất lựa chọn tham lam (Greedy Choice Property): Tồn tại ít nhất một nghiệm tối ưu toàn cục chứa lựa chọn tham lam đầu tiên.
  2. Cấu trúc con tối ưu (Optimal Substructure): Sau khi thực hiện lựa chọn tham lam, bài toán thu hẹp về một bài toán con đồng dạng có quy mô nhỏ hơn mà việc giải bài toán con đó cũng dẫn đến tối ưu toàn cục.

Lập lịch sự kiện tham lam

2. Các mô hình bài toán tham lam

2.1. Mô hình 1: Lựa chọn khoảng không giao nhau nhiều nhất (Interval Scheduling)

Cho $N$ sự kiện, mỗi sự kiện diễn ra trong khoảng thời gian $[L_i, R_i]$. Hãy chọn số lượng sự kiện nhiều nhất sao cho không có hai sự kiện nào bị trùng lấn thời gian.

  • Chiến lược tham lam đúng đắn: Luôn ưu tiên chọn sự kiện có thời điểm kết thúc sớm nhất ($R_i$ nhỏ nhất).
  • Chứng minh đổi chỗ (Exchange Argument): Giả sử tồn tại một phương án tối ưu $OPT$ không chọn sự kiện $k$ kết thúc sớm nhất mà chọn sự kiện $x$ kết thúc muộn hơn ($R_x > R_k$). Nếu ta thay thế sự kiện $x$ bằng sự kiện $k$, sự kiện $k$ kết thúc sớm hơn nên khoảng thời gian còn lại sau $k$ sẽ rộng hơn hoặc bằng khoảng thời gian sau $x$, do đó không làm ảnh hưởng đến bất kỳ sự kiện nào chọn sau đó $\implies$ Phương án mới sau khi đổi chỗ có số lượng sự kiện ít nhất bằng $OPT$.
bool cmp(const vector<long long> &a, const vector<long long> &b) {
    return a[1] < b[1]; // Sắp xếp theo thời điểm kết thúc tăng dần
}

int max_events(vector<vector<long long>> &events) {
    sort(events.begin(), events.end(), cmp);
    int count = 0;
    long long last_end = -1e18;
    for (const auto &e : events) {
        if (e[0] >= last_end) { // Nếu thời điểm bắt đầu >= thời điểm kết thúc của sự kiện trước
            count++;
            last_end = e[1];
        }
    }
    return count;
}

3. Mẫu cài đặt chuẩn thi đấu: Tham lam xếp hàng phục vụ (SJF — Shortest Job First)

Bài toán: Có $N$ khách hàng, khách hàng thứ $i$ cần thời gian phục vụ là $T_i$. Tìm thứ tự phục vụ để tổng thời gian chờ đợi của tất cả khách hàng là nhỏ nhất.

  • Chiến lược: Khách hàng có thời gian phục vụ ngắn nhất đứng đầu tiên.
  • Công thức tổng thời gian chờ: $\sum_{i=0}^{N-1} (N - 1 - i) \times T_i$.
#include <bits/stdc++.h>
using namespace std;

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

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

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

    sort(t.begin(), t.end()); // Sắp xếp tăng dần

    long long total_wait_time = 0;
    long long current_time = 0;

    for (int i = 0; i < n; ++i) {
        total_wait_time += current_time;
        current_time += t[i];
    }

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

4. Ranh giới áp dụng: Khi nào dùng Greedy vs Quy hoạch động (DP)?

Bài Toán Dùng Tham Lam (Greedy) Khi Nào? Buộc Phải Dùng Quy Hoạch Động (DP) Khi Nào?
Cái túi (Knapsack) Các đồ vật có thể chia nhỏ (Fractional Knapsack) $\implies$ Sắp xếp theo đơn giá giá trị/khối lượng $V_i / W_i$ giảm dần. Các đồ vật nguyên vẹn không được chia nhỏ (0/1 Knapsack) $\implies$ Buộc dùng DP $\mathcal{O}(NW)$.
Đổi tiền (Coin Change) Hệ mệnh giá là hệ chính quy (Canonical / bội số như $1, 2, 5, 10, 20$). Hệ mệnh giá tùy ý (ví dụ: mệnh giá $1, 3, 4$ với $S = 6$, Greedy chọn $4+1+1=3$ tờ, nhưng tối ưu là $3+3=2$ tờ).

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.

Lựa chọn sự kiện không trùng giờ
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng thời gian chờ nhỏ nhất (sjf)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Cái túi chia nhỏ được (fractional knapsack)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Phủ đoạn thẳng ít nhất (minimum interval cover)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Ghép thuyền cứu hộ cực trị
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Nối các sợi dây tiết kiệm chi phí nhất
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Lập lịch công việc có deadline & tiền phạt
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tối đa hóa lợi nhuận giao hàng
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Chia kẹo thưởng cho học sinh theo điểm số
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tối ưu hóa mua bán cổ phiếu không giới hạn lần giao dịch
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Sắp đặt chuỗi ký tự không trùng lặp kề nhau
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số lượng trạm tiếp nhiên liệu ít nhất (gas station)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Lập lịch phòng họp tối thiểu (meeting rooms ii)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Phục hồi dãy số đơn điệu với chi phí nhỏ nhất (slope trick cơ bản)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Ghép cặp trọng số trên đồ thị cây bằng greedy
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Thuật toán huffman coding nén dữ liệu tối ưu
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Cay ma huffman coding
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Lap lich deadline tien phat
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Thu gom vang tren luoi greedy
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Sap xep phan tu doi cho k lan
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Xep chong hop trong so va suc chiu
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Noi day nang cao k dau
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài