注意:我将稍微更改您的动态规划关系,以便在j = 0
时没有特殊情况。现在dp[j]
是前j
个项A [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])
(因此在 j 而不是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]
之前,我们使用A[i-1]
更新C
。为此,我们查找集合x
中最小的元素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]
时,我们只需通过弹出头部来删除所有满足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++;
}
}
这次我针对你的算法进行了压力测试:
点击这里查看。如有不清楚的地方,请告知。