Editorial for Đèn hiệu đơn độc
Submitting an official solution before solving the problem yourself is a bannable offence.
Gợi ý 1 - Xét riêng đóng góp của từng đèn
Một đoạn cảm biến chỉ được tính khi nó chứa đúng một đèn. Gắn đóng góp đó cho vị trí của đèn duy nhất nằm trong đoạn.
Gợi ý 2 - Chỉ cần biết hai đèn lân cận
Giả sử đèn đang xét ở vị trí b, đèn được chọn gần nhất bên trái ở a và bên phải ở c. Một cảm biến [l,r] chỉ chứa riêng đèn b khi a < l <= b <= r < c. Thêm hai vị trí lính canh 0 và N+1 ở hai đầu.
Lời giải đầy đủ
Hàm đóng góp
Với ba vị trí a < b < c, gọi gain(a,b,c) là tổng trọng số của các cảm biến thỏa a < l <= b <= r < c. Đây chính xác là các cảm biến chứa b nhưng không chứa đèn được chọn nào khác.
Ta tiền tính gain cho mọi bộ ba bằng cách duyệt M cảm biến. Với N <= 30, phần này chỉ mất O(MN^3).
Quy hoạch động
Gọi dp[t][a][b] là giá trị tốt nhất sau khi đã chọn t vị trí, hai vị trí cuối là a,b. Chi phí của các đèn đã chọn được trừ hết; phần thưởng của mọi đèn trừ b đã được chốt. Với lựa chọn đầu tiên b, khởi tạo dp[1][0][b] = -cost[b].
Khi thêm đèn c, ta chốt phần thưởng của b và trừ chi phí của c:
dp[t+1][b][c] = max(dp[t+1][b][c], dp[t][a][b] + gain(a,b,c) - cost[c]).
Sau khi chọn đủ K đèn, chốt đèn cuối cùng bằng vị trí lính canh N+1 và lấy kết quả lớn nhất.
Độ phức tạp
Tiền tính gain mất O(MN^3); DP mất O(KN^3). Bộ nhớ O(KN^2 + N^3).
Code C++17
#include <bits/stdc++.h>
using namespace std;
const long long NEG = -(1LL << 60);
struct Sensor {
int l, r;
long long w;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
vector<long long> cost(n + 1);
for (int i = 1; i <= n; ++i) cin >> cost[i];
vector<Sensor> sensors(m);
for (auto& s : sensors) cin >> s.l >> s.r >> s.w;
vector<vector<vector<long long>>> gain(
n + 1, vector<vector<long long>>(n + 1, vector<long long>(n + 2, 0))
);
for (int a = 0; a <= n; ++a) {
for (int b = a + 1; b <= n; ++b) {
for (int c = b + 1; c <= n + 1; ++c) {
for (const Sensor& s : sensors) {
if (a < s.l && s.l <= b && b <= s.r && s.r < c)
gain[a][b][c] += s.w;
}
}
}
}
vector<vector<vector<long long>>> dp(
k + 1, vector<vector<long long>>(n + 1, vector<long long>(n + 1, NEG))
);
for (int b = 1; b <= n; ++b)
dp[1][0][b] = -cost[b];
for (int t = 1; t < k; ++t) {
for (int a = 0; a <= n; ++a) {
for (int b = a + 1; b <= n; ++b) {
if (dp[t][a][b] == NEG) continue;
for (int c = b + 1; c <= n; ++c) {
dp[t + 1][b][c] = max(
dp[t + 1][b][c],
dp[t][a][b] + gain[a][b][c] - cost[c]
);
}
}
}
}
long long answer = NEG;
for (int a = 0; a <= n; ++a) {
for (int b = a + 1; b <= n; ++b) {
if (dp[k][a][b] != NEG)
answer = max(answer, dp[k][a][b] + gain[a][b][n + 1]);
}
}
cout << answer << endl;
return 0;
}