Hướng dẫn giải của Chọn sách
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Hướng dẫn giải
Chọn nhiều sách nhất để xếp thành chồng sao cho sách trên có cả chiều dài và chiều rộng nhỏ hơn hẳn sách dưới. Đây là bài dãy con tăng dài nhất trên hai chiều.
Sub 1: Quy hoạch động — ~O(n^2)~, dùng khi ~n \le 2000~
Sắp xếp các sách theo ~d~ tăng dần, nếu ~d~ bằng nhau thì ~r~ giảm dần. Gọi ~f[i]~ là số sách nhiều nhất của chồng có sách ~i~ nằm dưới cùng; ~f[i] = 1 + \max f[j]~ với ~j < i~, ~d_j < d_i~, ~r_j < r_i~. Đáp án là ~\max f[i]~. Nếu chỉ nộp sub này: đúng 10/20 test.
Sub 2: Dãy con tăng ngặt dài nhất — ~O(n \log n)~
Sau khi sắp xếp như trên, chỉ cần tìm dãy con tăng ngặt dài nhất theo ~r~ (hai sách cùng ~d~ có ~r~ giảm dần nên không thể cùng nằm trong dãy tăng ngặt). Dùng mảng ~tail~ và lower_bound (tìm vị trí đầu tiên ~\ge r~, đảm bảo ngặt). Nộp riêng sub này đúng 20/20 test.
Chương trình hoàn chỉnh
Hàm ~main~ đọc dữ liệu từ file, sắp xếp; nếu ~n \le 2000~ gọi sub1, ngược lại gọi sub2. Khi đi thi nên viết như vậy: sub sau có sai hoặc chưa kịp làm thì vẫn ăn điểm sub trước. Chương trình đạt 20/20 test.
#include <bits/stdc++.h>
using namespace std;
int n;
pair<int, int> b[200005]; // (d, r)
int f[200005];
// Sub 1: sắp xếp theo d tăng (d bằng nhau thì r giảm), quy hoạch động O(n^2):
// f[i] = số sách nhiều nhất của chồng có sách i ở dưới cùng. Dùng khi n <= 2000
void sub1() {
int best = 0;
for (int i = 0; i < n; i++) {
f[i] = 1;
for (int j = 0; j < i; j++)
if (b[j].first < b[i].first && b[j].second < b[i].second)
f[i] = max(f[i], f[j] + 1);
best = max(best, f[i]);
}
cout << best << "\n";
}
// Sub 2: sau khi sắp xếp, đáp án là dãy con tăng NGẶT dài nhất theo r, tìm bằng chặt nhị phân O(n log n)
void sub2() {
vector<int> tail;
for (int i = 0; i < n; i++) {
auto it = lower_bound(tail.begin(), tail.end(), b[i].second);
if (it == tail.end()) tail.push_back(b[i].second);
else *it = b[i].second;
}
cout << tail.size() << "\n";
}
int main() {
freopen("ChonSach.INP", "r", stdin);
freopen("ChonSach.OUT", "w", stdout);
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n;
for (int i = 0; i < n; i++) cin >> b[i].first >> b[i].second;
// d bằng nhau thì xếp r giảm dần để hai sách cùng d không bao giờ cùng nằm trong một dãy tăng ngặt
sort(b, b + n, [](const pair<int,int> &x, const pair<int,int> &y) {
if (x.first != y.first) return x.first < y.first;
return x.second > y.second;
});
if (n <= 2000) sub1();
else sub2();
return 0;
}
Bình luận