let menu = ["Home", "Algorithms", "CodeHub", "VNOI Statistics"];
Gọi \(A[i]\) là dãy input.
Do ta chỉ có thể cắt ngang theo một cạnh của tấm ván, kết quả của bài toán là một trong các giá trị \(A[i]\).
Giả sử đoạn \(A[l]\) đến \(A[r]\) có tấm ván ngắn nhất là \(A[i]\), ta có thể cắt một tấm ván vuông cạnh \(A[i]\) nếu \(r-l+1 \ge A[i]\).
Từ nhận xét trên, ta có thể giải bài toán dùng 2 vòng lặp (code n2.cpp
).
Để giảm độ phức tạp thuật toán về \(O(N)\), gọi \(L[i]\) là số nhỏ nhất sao cho \(A[i]\) là min trong đoạn \(A[L[i]]\) đến \(A[i]\). Tương tự, \(R[i]\) là số lớn nhất sao cho \(A[i]\) là min trong đoạn \(A[i]\) đến \(A[R[i]]\). Khi đó, ta có thể cắt một tấm ván vuông dài \(A[i]\) nếu \(R[i] - L[i] + 1 \ge A[i]\).
\(L\) và \(R\) được tìm bằng stack.
Duyệt \(i\) từ 1 đến N, để tìm \(L[i]\) ta sẽ lặp \(j\) từ \(i\) về 1, dừng khi tìm được \(A[j] < A[i]\), ta có \(L[i] = j + 1\).
Nhận xét: ta có thể bỏ đi những giá trị ở giữa \(A[i]\) và \(A[j]\) trong lần duyệt sau.
Chứng minh: xét \(k > i\), nếu \(A[i] < A[k]\) thì việc lặp để tìm \(L[k]\) sẽ dừng tại \(i\) hoặc sớm hơn. Ngược lại, nếu \(A[i] \ge A[k]\), có thể bỏ qua những giá trị \(z, j < z < i\), do \(A[z] \ge A[i] \implies A[z] \ge A[k]\).
Từ nhận xét trên, ta lưu mảng \(S\) chứa những chỉ số cần duyệt. Khi duyệt, ta so sánh \(A[i]\) với \(A[last]\) (\(last\) là phần tử cuối cùng của \(S\)), nếu \(A[last] \ge A[i]\) thì xóa \(last\) ra khỏi \(S\). Dừng khi tìm được \(A[last] < A[i]\) hoặc mảng \(S\) đã rỗng. Sau đó thêm \(i\) vào cuối mảng \(S\).
Để tìm \(R\) ta duyệt từ \(n\) về 1 và làm tương tự. Code n.cpp
cài đặt thuật toán này.
Xem thêm về kĩ thuật này ở Kĩ thuật sử dụng Deque tìm Min/Max trên đoạn tịnh tiến.
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
vector<int> a(n);
for (int &x: a) cin >> x;
int result = 1;
for (int i=0; i<n; i++) {
int min_a = a[i];
for (int j=i; j>=0; j--) {
min_a = min(min_a, a[j]);
if (i-j+1 >= min_a) result = max(result, min_a);
}
}
cout << result << endl;
}
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
vector<int> a(n);
for (int &x: a) cin >> x;
vector<int> stack;
vector<int> l(n);
for (int i=0; i<n; i++) {
while (!stack.empty() && a[stack.back()] >= a[i]) {
stack.pop_back();
}
l[i] = stack.empty()? 0 : stack.back()+1;
stack.push_back(i);
}
stack.clear();
vector<int> r(n);
for (int i=n-1; i>=0; i--) {
while (!stack.empty() && a[stack.back()] >= a[i]) {
stack.pop_back();
}
r[i] = stack.empty()? n-1 : stack.back()-1;
stack.push_back(i);
}
int result = 1;
for (int i=0; i<n; i++) {
if (a[i] <= r[i] - l[i] + 1) {
result = max(result, a[i]);
}
}
cout << result << endl;
}