Hướng giải của Ghép cặp mũ bảo hiểm
Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Tác giả:
Ta sử dụng phương pháp Tham lam kết hợp với kỹ thuật Hai con trỏ (Two Pointers):
- Sắp xếp cả hai mảng kích thước mũ \(A\) và cỡ đầu \(B\) theo thứ tự tăng dần.
- Dùng hai con trỏ \(i\) (chỉ mảng mũ \(A\)) và \(j\) (chỉ mảng đầu \(B\)), ban đầu đặt ở đầu mỗi mảng (\(i = 0, j = 0\)).
- Tại mỗi bước:
- Nếu mũ \(A[i]\) vừa với đầu \(B[j]\) (tức là \(|A[i] - B[j]| \le K\)), ta ghép đôi chúng thành công, tăng số lượng kết quả lên 1 và tiến cả hai con trỏ (\(i++, j++\)).
- Nếu mũ quá nhỏ so với đầu (\(A[i] < B[j] - K\)), ta không thể ghép chiếc mũ này cho bất cứ ai từ vị trí \(j\) trở về sau được nữa (vì cỡ đầu sau còn lớn hơn). Ta tiến con trỏ mũ \(i++\).
- Nếu đầu quá nhỏ so với mũ (\(B[j] < A[i] - K\)), ta tiến con trỏ đầu \(j++\).
Độ phức tạp thời gian: \(O(N \log N)\).
Xem code mẫu C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
long long k;
if (!(cin >> n >> k)) return 0;
vector<long long> a(n), b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
sort(a.begin(), a.end());
sort(b.begin(), b.end());
int ans = 0;
int i = 0, j = 0;
while (i < n && j < n) {
if (abs(a[i] - b[j]) <= k) {
ans++;
i++;
j++;
} else if (a[i] < b[j]) {
i++;
} else {
j++;
}
}
cout << ans << "\n";
return 0;
}
Nhận xét