Editorial for Chia buổi tập
Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.
Submitting an official solution before solving the problem yourself is a bannable offence.
Gợi ý 1 - Chi phí của một buổi chỉ phụ thuộc vào đoạn nó nhận
Nếu buổi cuối cùng tập các tiết mục từ j+1 đến i, chi phí của buổi đó là giá trị lớn nhất trừ giá trị nhỏ nhất trong đoạn này.
Gợi ý 2 - Quy hoạch động theo số buổi và số tiết mục đã xếp
Đặt dp[g][i] là chi phí nhỏ nhất để chia i tiết mục đầu thành đúng g buổi. Thử mọi vị trí j làm điểm kết thúc của g-1 buổi đầu.
Lời giải đầy đủ
Công thức quy hoạch động
Đặt dp[g][i] là tổng chi phí nhỏ nhất khi chia i tiết mục đầu thành đúng g buổi liên tiếp, không rỗng.
Nếu buổi cuối bắt đầu sau tiết mục j, thì:
dp[g][i] = min(dp[g-1][j] + max(a[j+1..i]) - min(a[j+1..i])),
với g-1 <= j < i. Ta duyệt j lùi từ i-1 về g-1; trong lúc duyệt, cập nhật nhanh giá trị nhỏ nhất và lớn nhất của đoạn cuối.
Khởi tạo dp[0][0]=0; các trạng thái còn lại ban đầu là vô cực. Có thể chỉ giữ hàng g-1 và hàng g hiện tại.
Độ phức tạp
- Thời gian:
O(KN^2). VớiN <= 500vàK <= 30, số chuyển trạng thái đủ nhỏ. - Bộ nhớ:
O(N).
Code C++17
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<long long> a(n + 1);
for (int i = 1; i <= n; ++i) cin >> a[i];
const long long INF = (1LL << 62);
vector<long long> previous(n + 1, INF), current(n + 1, INF);
previous[0] = 0;
for (int groups = 1; groups <= k; ++groups) {
fill(current.begin(), current.end(), INF);
for (int i = groups; i <= n; ++i) {
long long minimum = LLONG_MAX;
long long maximum = LLONG_MIN;
for (int j = i - 1; j >= groups - 1; --j) {
minimum = min(minimum, a[j + 1]);
maximum = max(maximum, a[j + 1]);
if (previous[j] != INF) {
current[i] = min(current[i],
previous[j] + maximum - minimum);
}
}
}
previous.swap(current);
}
cout << previous[n] << '\n';
return 0;
}
Loading...