注意:我会稍微改变你的动态规划关系,这样j = 0就没有特殊情况了。现在dp[j] 是第一个j termsA[0], ..., A[j-1] 的答案,并且:
dp[i] = min(dp[j] + max(A[j], ..., A[i-1]), i-k <= j < i)
问题的答案现在是dp[n]。
请注意,如果j < i 和dp[j] >= dp[i],则在以下转换中您将不需要dp[j],因为max(A[j], ..., A[l]) >= max(A[i], ..., A[l])(因此在i 而不是j 处剪切总是更好。
此外,让C[j] = max(A[j+1], ..., A[l])(其中l 是我们在动态编程步骤中的当前索引,即C++ 程序中的i)。
然后您可以在内存中保留一组索引x1 < ... < xm(动态规划关系转换的“有趣”索引),例如:dp[x1] < ... < dp[xm] (1)。然后自动C[x1] >= ... >= C[xm](2)。
要存储{x1, ..., xm},我们需要一些支持以下操作的数据结构:
- 弹回(当我们从
i 移动到i+1 时,我们必须说i-k 现在无法访问)或前面(参见插入)。
- 推前
x(当我们计算出dp[i]时,我们在保留(1)的同时插入它,通过删除相应的元素)。
- 计算
min(dp[xj] + C[xj], 1 <= j <= m)。
因此,一些队列来存储x1, ..., xk 以及一个set 来存储所有dp[xi] + C[xi] 就足够了。
当我们插入元素 i 时,我们如何既保留 (1) 又更新 C?
- 在计算
dp[i] 之前,我们将C 更新为A[i-1]。为此,我们在集合x s.t. 中找到最小元素xj。 C[xj] <= A[i-1]。然后 (1) 和 (2) 对所有 j' >= j 隐含 dp[j'] + C[j'] >= dp[j] + C[j],因此我们将 C[xj] 更新为 A[i-1] 并从集合 (*) 中删除 x(j+1), ..., xm。
- 当我们插入
dp[i] 时,我们只是删除所有元素 s.t. dp[j] >= dp[i] 弹出前面。
- 当我们删除
i-k 时,可能在 (*) 中破坏的某些元素现在变得最好了。因此,如有必要,我们会更新 C 并插入最后一个元素。
复杂度:O(n log n)(集合中最多可能有 2n 插入)。
这段代码总结了主要思想:
template<class T> void relaxmax(T& r, T v) { r = max(r, v); }
vector<int> dp(n + 1);
vector<int> C(n + 1, -INF);
vector<int> q(n + 1);
vector<int> ne(n + 1, -INF);
int qback = 0, qfront = 0;
auto cmp = [&](const int& x, const int& y) {
int vx = dp[x] + C[x], vy = dp[y] + C[y];
return vx != vy ? vx < vy : x < y;
};
set<int, decltype(cmp)> s(cmp);
dp[0] = 0;
s.insert(0);
q[qfront++] = 0;
for (int i = 1; i <= n; ++i) {
C[i] = A[i - 1];
auto it_last = lower_bound(q.begin() + qback, q.begin() + qfront, i, [=](const int& x, const int& y) {
return C[x] > C[y];
});
for (auto it = it_last; it != q.begin() + qfront; ++it) {
s.erase(*it);
C[*it] = A[i - 1];
ne[*it] = i;
if (it == it_last) s.insert(*it);
}
dp[i] = dp[*s.begin()] + C[*s.begin()];
while (qback < qfront && dp[q[qfront]] >= dp[i]) {
s.erase(q[qfront]);
qfront--;
}
q[qfront++] = i;
C[i] = -INF;
s.insert(i);
if (q[qback] == i - k) {
s.erase(i - k);
if (qback + 1 != qfront && ne[q[qback]] > q[qback + 1]) {
s.erase(q[qback + 1]);
relaxmax(C[q[qback + 1]], C[i - k]);
s.insert(q[qback + 1]);
}
qback++;
}
}
// answer: dp[n]
这次我根据你的算法对它进行了压力测试:see here。
如果还不清楚,请告诉我。