Câu 1. HEAP.CPP
HEAP.*Cho dãy a1, a2, ..., aN. Hãy sử dụng heap để sắp xếp dãy trên thành dãy không giảm rồi in ra dãy sau khi sắp xếp.
HEAP.INP
5 1 4 6 3 2
HEAP.OUT
1 2 3 4 6
Bài giảng hệ thống hóa kiến thức priority_queue trong C++: heap max, heap min, comparator, các hàm thường dùng và các bài tập ứng dụng.
priority_queue là hàng đợi ưu tiên. Mỗi lần lấy phần tử, ta không lấy theo thứ tự vào trước ra trước như queue, mà lấy phần tử có độ ưu tiên cao nhất.
Trong C++, priority_queue được cài đặt bằng cấu trúc heap. Mặc định là max-heap, nghĩa là phần tử lớn nhất nằm ở đỉnh.
Heap max: phần tử ở đỉnh là lớn nhất. Đây là mặc định của priority_queue<int>.
Heap min: phần tử ở đỉnh là nhỏ nhất. Dùng dạng priority_queue<int, vector<int>, greater<int>>.
Nếu chỉ cần sắp xếp toàn bộ dãy một lần, dùng sort() là đơn giản. Nếu dữ liệu được thêm dần và ta cần lấy phần tử lớn nhất/nhỏ nhất liên tục, priority_queue rất phù hợp.
| Tình huống | Nên dùng | Lý do |
|---|---|---|
| Sắp xếp toàn bộ dãy | sort | Dễ viết, O(n log n) |
| Liên tục thêm và lấy max/min | priority_queue | push/pop O(log n), top O(1) |
| Tìm k phần tử nhỏ nhất/lớn nhất | priority_queue | Không cần sinh toàn bộ dữ liệu |
| Huffman, Dijkstra, KMIN | priority_queue | Mỗi bước cần chọn phần tử ưu tiên nhất |
#include <bits/stdc++.h> using namespace std; priority_queue<int> pq; priority_queue<long long> pq2; priority_queue<double> pq3;
pq.top() luôn trả về phần tử lớn nhất hiện có.
priority_queue<int, vector<int>, greater<int>> pq; // Với pair: priority_queue< pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > q;
pq.top() trả về phần tử nhỏ nhất.
Khi cần ưu tiên theo quy tắc riêng, ta tạo struct comparator.
struct cmp { bool operator()(int a, int b) { return a < b; // ưu tiên số lớn hơn trước } }; priority_queue<int, vector<int>, cmp> pq;
| Hàm | Ý nghĩa | Độ phức tạp | Ví dụ |
|---|---|---|---|
| size() | Trả về số lượng phần tử trong heap. | O(1) | pq.size() |
| empty() | Kiểm tra heap có rỗng không. | O(1) | pq.empty() |
| push(x) | Thêm phần tử x vào heap. | O(log n) | pq.push(5) |
| top() | Lấy giá trị ở đỉnh heap. | O(1) | pq.top() |
| pop() | Xóa phần tử ở đỉnh heap. | O(log n) | pq.pop() |
#include <bits/stdc++.h> using namespace std; int main() { priority_queue<int> pq; pq.push(3); pq.push(5); pq.push(1); cout << pq.top() << "\n"; // 5 pq.pop(); cout << pq.top() << "\n"; // 3 }
#include <bits/stdc++.h> using namespace std; int main() { priority_queue<int, vector<int>, greater<int>> pq; pq.push(3); pq.push(5); pq.push(1); cout << pq.top() << "\n"; // 1 pq.pop(); cout << pq.top() << "\n"; // 3 }
priority_queue<pair<int, int>> pq; pq.push({5, 1}); pq.push({7, 2}); pq.push({7, 3}); // pair được so sánh theo first trước, nếu bằng thì so sánh second while (!pq.empty()) { auto x = pq.top(); pq.pop(); cout << x.first << " " << x.second << "\n"; }
Đưa toàn bộ phần tử vào heap rồi lấy dần ra để tạo dãy có thứ tự.
Luôn ghép hai phần tử nhỏ nhất trước, ví dụ bài cắt gỗ/Huffman.
Dùng heap để lấy dần k ứng viên tốt nhất mà không cần sinh toàn bộ tổ hợp.
Lấy đỉnh có khoảng cách tạm thời nhỏ nhất bằng min-heap.
Sự kiện nào có thời điểm nhỏ nhất thì xử lý trước.
Dữ liệu thay đổi liên tục, cần lấy max/min nhanh.
| Bài | Tên bài | Kỹ năng chính | Mức độ |
|---|---|---|---|
| 1 | HEAP - Sắp xếp bằng heap | min-heap / max-heap | Dễ |
| 2 | QBHEAP - Thao tác heap | max-heap, xóa toàn bộ giá trị lớn nhất | Trung bình |
| 3 | WOOD - Cắt gỗ tối ưu | min-heap, greedy | Trung bình |
| 4 | KMIN - K tổng nhỏ nhất | priority_queue với pair | Khá |
Cho dãy a1, a2, ..., aN. Hãy sử dụng heap để sắp xếp dãy trên thành dãy không giảm rồi in ra dãy sau khi sắp xếp.
5 1 4 6 3 2
1 2 3 4 6
Cho trước một danh sách rỗng. Có hai thao tác: +V thêm giá trị V vào danh sách; - nếu danh sách không rỗng thì loại bỏ tất cả các phần tử lớn nhất hiện có. Sau khi thực hiện tất cả thao tác, in số lượng phần tử còn lại và các giá trị theo thứ tự giảm dần.
+1 +3 +2 +3 - +4 +4 - +2 +9 +7 +8 -
5 8 7 2 2 1
Một người nông dân muốn cắt tấm gỗ thành N miếng có độ dài lần lượt là a1, a2, ..., aN. Để cắt một miếng gỗ dài X thành hai phần thì mất X tiền. Hãy tính chi phí nhỏ nhất để cắt được các miếng gỗ mong muốn.
1 4 1 2 3 4
19
Cho hai dãy số nguyên A và B. Với mỗi Ai và Bj, tạo tổng Ai + Bj. Tất cả các tổng này sau khi sắp xếp không giảm tạo thành dãy C. Hãy tìm K số đầu tiên trong dãy C.
4 4 6 1 2 3 4 2 3 4 5
3 4 4 5 5 5