Algorithm 优化:将数组分成长度不大于k的连续子序列,使每个子序列的最大值之和最小

Algorithm 优化:将数组分成长度不大于k的连续子序列,使每个子序列的最大值之和最小,algorithm,dynamic-programming,Algorithm,Dynamic Programming,优化O(n^2)算法到O(n log n) 问题陈述 给定由n正整数组成的数组A。将数组划分为长度不大于k的连续子序列,使每个子序列的最大值之和最小。这里有一个例子 如果n=8和k=5并且数组的元素是1 4 1 3 4 7 2,则最佳解决方案是1 | 4 1 3 4 7 | 2 2。总和将是max{1}+max{4,1,3,4,7}+max{2,2}=1+7+2=10 O(n^2)溶液 设dp[i]为子问题数组A[0]的问题陈述中的最小和。。。A[i]dp[0]=A[0],对于0最大值) max

优化
O(n^2)
算法到
O(n log n)

问题陈述

给定由
n
正整数组成的数组
A
。将数组划分为长度不大于
k
的连续子序列,使每个子序列的最大值之和最小。这里有一个例子

如果
n=8
k=5
并且数组的元素是
1 4 1 3 4 7 2
,则最佳解决方案是
1 | 4 1 3 4 7 | 2 2
。总和将是
max{1}+max{4,1,3,4,7}+max{2,2}=1+7+2=10

O(n^2)溶液

dp[i]
为子问题数组
A[0]的问题陈述中的最小和。。。A[i]
dp[0]=A[0]
,对于
0
dp[-1]=0

//A,n,k,-已定义
//dp-全部初始化为INF
dp[0]=A[0];
用于(自动i=1;i=0&&j>=i-k+1;j--){
如果(A[j]>最大值)
max=A[j];
自动求和=最大值+(j>0?dp[j-1]:0);
如果(总和
O(n日志n)?


问题作者声称在
O(nlogn)
时间内解决这个问题是可能的,并且有一些人能够通过测试用例。如何对其进行优化?

注意:我将稍微更改您的动态规划关系,以便在
j=0
时没有特殊情况。现在
dp[j]
是第一个
j
术语
A[0]、…、A[j-1]
的答案:

dp[i]=min(dp[j]+max(A[j],…,A[i-1]),i-k=dp[i]
,在下面的转换中,您不需要
dp[j]
,因为
max(A[j],…,A[l])=max(A[i],…,A[l])
(因此在
i
处切割总是比在
j
处切割更好

<> >让<代码> C[j]=max(A[j+1],…,[L])/COD>(其中<代码> L>代码>是我们当前的动态编程步骤中的索引,即,C++程序中的<代码> I/C> >

然后,您可以在内存中保存一些索引集
x1<…
(动态规划关系转换的“有趣”索引),这样:
dp[x1]<…
(1)。然后自动
C[x1]>=…>=C[xm]
(2)

要存储
{x1,…,xm}
,我们需要一些支持以下操作的数据结构:

  • 向后弹出(当我们从
    i
    移动到
    i+1
    时,我们必须说
    i-k
    现在无法访问)或向前弹出(参见插入)
  • 向前推
    x
    (当我们计算了
    dp[i]
    ,我们通过删除相应的元素,在保留(1)的同时插入它)

  • 计算
    min(dp[xj]+C[xj],1您当前的算法具有O(n*k)复杂度,因为第二个循环回溯的步数不超过
    k
    步。在找到使dp[i]最小的j之后,如果该j导致最后一段的元素数严格小于k,那么该j也将为dp[i+1]提供有效的解决方案.设m是dp[i]最优解的最后一段中的最大元素。A[i+1]可以是>m,或者想知道这个问题在现实世界中的一个示例应用,知道吗?你的代码似乎在我问题中给出的示例上返回了14。我不确定我是否理解你的正确操作,但我是这样做的:。此外,我在索引1处开始
    A
    dp
    C
    ,并且
    C[j]=max{A[j],…,A[i]}
    在这段代码中。我不确定如何处理属性(2),因此我希望对此进行一些澄清。(
    >=dp[i]
    仅此测试消除就让我的解决方案通过了测试用例,但出于好奇。谢谢!)@AquaBlitz11:我更新了我的答案。现在它应该可以工作了(请注意,您不需要
    multiset
    ,因为这里的所有元素都是不同的)。
    // A, n, k, - defined
    // dp - all initialized to INF
    dp[0] = A[0];
    for (auto i = 1; i < n; i++) {
        auto max = -INF;
        for (auto j = i; j >= 0 && j >= i-k+1; j--) {
            if (A[j] > max)
                max = A[j];
            auto sum = max + (j > 0 ? dp[j-1] : 0);
            if (sum < dp[i])
                dp[i] = sum;
        }
    }
    // answer: dp[n-1]
    
    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]