问:所有段中所有最大数的总和O(N)

时间:2017-09-29 06:37:04

标签: algorithm

给定一个整数数组,找到 O(n)中所有段(间隔)中所有最大数字的总和。

2 个答案:

答案 0 :(得分:2)

O(n)

中的解决方案

接下来是C ++源代码,程序读取标准输入或a 其路径作为命令行上的第一个参数提供的文件。

输入文件的格式应为:

  1. N表示数组中元素数的整数
  2. 由表示数组元素的空格分隔的N个整数
  3. 编译通过以下方式完成:

    g++ -std=c++14 -g -Wall -O0    solution.cpp   -o solution
    

    程序将首先使用O(n)算法计算总和,然后使用。{ 用于验证的O(n^3)算法。

    示例运行:

    $ ./solution.exe 
    3 
    4 5 6
    O(n) sum: 32
    O(n^3) sum: 32
    

    源代码:

    #include <cstdio>
    
    #include <algorithm>
    #include <iomanip>
    #include <iostream>
    #include <vector>
    #include <stack>
    
    using namespace std;
    
    int main(int argc, char* argv[]) {
        if(argc > 1)
            freopen(argv[1], "r", stdin);
    
        // load input array
        int N;
        cin >> N;
        vector<int> A(N);
        for(auto& ai : A)
            cin >> ai;
    
        // compute sum max of all subarrays in O(n)
        vector<int> l(N);
        vector<int> r(N);
        stack<int> s;
    
        for(int i=0; i<N; ++i) {
            while(s.size() && A[s.top()] < A[i]) {
                r[s.top()] = i;
                s.pop();
            }
            s.push(i);
        }
        while(s.size()) {
            r[s.top()] = N;
            s.pop();
        }
    
        for(int i=N-1; i>=0; --i) {
            while(s.size() && A[s.top()] <= A[i]) {
                l[s.top()] = i;
                s.pop();
            }
            s.push(i);
        }
        while(s.size()) {
            l[s.top()] = -1;
            s.pop();
        }
    
        int sum = 0;
        for(int i=0; i<N; ++i) {
            int cs = A[i]*(i-l[i])*(r[i]-i);
            sum += cs;
        }
        cout << "O(n) sum: " << sum << '\n';
    
        // compute sum using O(n^3) algorithm for verification
        sum = 0;
        for(int i=0; i<N; ++i) {
            for(int j=i; j<N; ++j) {
                int cs = *max_element(begin(A)+i, begin(A)+j+1);
                sum += cs;
            }
        }
        cout << "O(n^3) sum: " << sum << '\n';
    }
    

    解决方案的证据

    首先,这不是完整的证明。我有一个证据,但它太多了 参与数学符号被包含在没有mathjs的网站上 支持......我将给出证明的草图,并将详细信息提供给 读者(我知道这很蹩脚)。

    该解决方案使用多个技巧

    • 所有子阵列的最大值之和等于每个子阵列的乘积之和 最大值乘以最大值的子阵列数
    • 可以将所有子阵列的集合划分为一组 子阵。此分区中的每个集合仅包含具有相同的子阵列 最大元素和集合的大小很容易计算

    让我们命名问题的元素:

    • 调用初始数组:A。值A[i]是数组中基于0的索引i的值。
    • 数组的大小为n。该数组从索引0扩展到n-1

    首先,我定义了一个子数组的 leader ,这是索引i 子数组中元素的值,使A[i]的值最大 子数组和索引j < i的子数组中的每个元素都有 A[j]<A[i]。直观地说,子数组的 leader 是其第一个的索引 最大值。

    我说 leader 的相等性定义了equivalence relation 子阵列。 (作为练习留下的证据)。

    由此,我们知道了集合的等价类form a partition 所有子阵列。此外,等价类中的所有元素都具有 相同的最大值(由于 leader 函数的定义)。

    等价类E_i的大小, leader 的所有子数组的集合 是i,可以从值中轻松计算出来:

    • l(i)这是i左侧的第一个索引A[l(i)] >= A[i]或。{ 如果不存在这样的索引,则为-1
    • r(i)这是i右侧A[l(i)] > A[i]n右侧的第一个索引 如果不存在此类索引,则E_i

    使用这些表示法(i-l(i))*(r(i)-i)的基数为:l(i)。证据留给读者作为练习。

    现在是计算值r(i)l(i)的编程技巧。作为 计算几乎相同我只会解释i的计算。我们 使用以下不变量来维护索引:

    • 堆栈中的索引代表 leader
    • 所有索引都按递增顺序
    • 与堆栈中的索引关联的所有值也按递增顺序

    我们从左到右扫描阵列。对于每个索引i,我们检查它的值 大于当前堆栈顶部的值。如果是这种情况,则意味着其 leader 是堆栈顶部的子阵列无法扩展到右侧的当前索引。因此,我们将 r(堆栈顶部)的值更新为i。我们弹出堆栈顶部,因为它可能不会涉及任何超过A[top of stack] >= A[i]的子数组。 我们继续更新 leader 并弹出堆栈顶部,直到 堆栈为空或i。然后我们在堆栈上推送r。 到达数组末尾时,堆栈中可能仍有一些索引。 这意味着它们参与延伸到数组末尾的子数组。 我们将N值更新为r()

    整个扫描会更新O(n)while的所有值。这是因为每一个 元素是

    • 更新一次
    • 推了一次

    由于我们不能多次弹出一个元素,因此内部n循环不会运行 整个阵列扫描的时间超过l()次。

    相同的过程用于计算-1,但以下情况除外:

    • 我们从右向左扫描
    • 由于 leader
    • 的定义,我们使用严格的弱比较
    • 我们使用O(n)来表示限制是数组的界限

    然后我们可以应用公式来计算等价的大小 类和在我们的总结中使用它。像我们一样导致O(1)算法 需要:

    • r(i)阅读O(1)
    • l(i)阅读O(1)
    • A(i)E_i
    • 中读取{{1}}子数组的最大值

答案 1 :(得分:0)

问题的复杂性是 not O(N),因为长度为N的数据集中的序列数为N * (N+1) / 2,可以很容易地显示出来:

  

N = 1:1序列([1]) - &gt; 1 * 2/2 = 1

     

N = 2:3序列([1],[2],[1,2]) - > 2 * 3/2 = 6/2 = 3

     

N = 3:6序列([1],[2],[3],[1,2],[2,3],[1,2,3]) - > 3 * 4/2 = 12/2 = 6

为了找到所有序列的最大值之和,需要迭代所有序列。证明完毕更新或者不......请参阅fjardon的回答,欣赏等价类的力量。

话说回来,你可以利用这样一个事实来避免额外的复杂性:在位置i0,长度为w的序列的最大值为max (seq i0 (w-1)) data.[i0+w-1])

这导致了下面F#片段的实现:

let maxsum (a: int[]) : int =
    let mutable x = 0
    let mutable total = 0
    let n = Array.length a
    for i0 in 0..n-1 do
        x <- a.[i0]
        total <- total + x
        for w in 2..n-i0 do
            x <- max x a.[i0+w-1]
            total <- total + x
    total