MaxDoubleSliceSum Codility 算法

Posted

技术标签:

【中文标题】MaxDoubleSliceSum Codility 算法【英文标题】:MaxDoubleSliceSum Codility Algorithm 【发布时间】:2014-09-08 14:34:41 【问题描述】:

我在 Codility Lessons 上偶然发现了这个问题,描述如下:

给定一个由 N 个整数组成的非空零索引数组 A。

一个三元组 (X, Y, Z),使得 0 ≤ X

双切片 (X, Y, Z) 的总和是 A[X + 1] + A[X + 2] + ... + A[Y - 1] + A[Y + 1] 的总和+ A[Y + 2] + ... + A[Z - 1]。

例如,数组 A 满足:

A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2

包含以下示例双切片:

双切片 (0, 3, 6),总和为 2 + 6 + 4 + 5 = 17,

双切片 (0, 3, 7),总和为 2 + 6 + 4 + 5 − 1 = 16,

双切片 (3, 4, 5),总和为 0。

目标是找到任何双切片的最大和。

写一个函数:

int 解(向量 &A);

给定一个由 N 个整数组成的非空零索引数组 A,返回任何双切片的最大和。

例如,给定:

A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2

该函数应该返回 17,因为数组 A 的双切片中没有一个总和大于 17。

假设:

N 是 [3..100,000] 范围内的整数; 数组 A 的每个元素都是 [−10,000..10,000] 范围内的整数。

复杂性:

预期的最坏情况时间复杂度为 O(N); 预期的最坏情况空间复杂度为 O(N),超出输入存储(不计算 > 输入参数所需的存储)。

输入数组的元素可以修改。

我已经阅读了关于从索引 i 开始计算 MaxSum 并在索引 i 结束的算法,但我不知道为什么我的方法有时会产生不好的结果。这个想法是计算在索引 i 处结束的 MaxSum,省略 0..i 范围内的最小值。这是我的代码:

int solution(vector<int> &A) 
    int n = A.size();

    int end = 2;   

    int ret = 0;
    int sum = 0;

    int min = A[1];

    while (end < n-1)
    
        if (A[end] < min)
        
            sum = max(0, sum + min);
            ret = max(ret, sum);
            min = A[end];
            ++end;
            continue;
        
        sum = max(0, sum + A[end]);
        ret = max(ret, sum);
        ++end;
    

    return ret;

如果你能帮我指出漏洞,我会很高兴!

【问题讨论】:

在您的示例中,4 不是 (3,4,5) 的总和吗? @Tarc 否,因为您必须省略 A[x]、A[y] 和 A[z]。所以你不能在那里总结任何东西。 【参考方案1】:

我的解决方案基于双向 Kadane 算法。更多细节在我的博客here。得分 100/100。

public int solution(int[] A) 
  int N = A.length;
  int[] K1 = new int[N];
  int[] K2 = new int[N];

  for(int i = 1; i < N-1; i++)
    K1[i] = Math.max(K1[i-1] + A[i], 0);
  
  for(int i = N-2; i > 0; i--)
    K2[i] = Math.max(K2[i+1]+A[i], 0);
  

  int max = 0;

  for(int i = 1; i < N-1; i++)
    max = Math.max(max, K1[i-1]+K2[i+1]);
  

  return max;

【讨论】:

非常好的解决方案!非常巧妙地使用了 Kadane 算法【参考方案2】:

这是我的代码:

int get_max_sum(const vector<int>& a) 
    int n = a.size();
    vector<int> best_pref(n);
    vector<int> best_suf(n);
    //Compute the best sum among all x values assuming that y = i.
    int min_pref = 0;
    int cur_pref = 0;
    for (int i = 1; i < n - 1; i++) 
        best_pref[i] = max(0, cur_pref - min_pref);
        cur_pref += a[i];
        min_pref = min(min_pref, cur_pref);
    
    //Compute the best sum among all z values assuming that y = i.
    int min_suf = 0;
    int cur_suf = 0;
    for (int i = n - 2; i > 0; i--) 
        best_suf[i] = max(0, cur_suf - min_suf);
        cur_suf += a[i];
        min_suf = min(min_suf, cur_suf);
    
    //Check all y values(y = i) and return the answer.
    int res = 0;
    for (int i = 1; i < n - 1; i++)
        res = max(res, best_pref[i] + best_suf[i]);
    return res;
 

 int get_max_sum_dummy(const vector<int>& a) 
    //Try all possible values of x, y and z.
    int res = 0;
    int n = a.size();
    for (int x = 0; x < n; x++)
        for (int y = x + 1; y < n; y++)
            for (int z = y + 1; z < n; z++) 
                int cur = 0;
                for (int i = x + 1; i < z; i++)
                    if (i != y)
                        cur += a[i];
                res = max(res, cur);
            
    return res;
 

bool test() 
    //Generate a lot of small test cases and compare the output of 
    //a brute force and the actual solution.
    bool ok = true;
    for (int test = 0; test < 10000; test++) 
        int size = rand() % 20 + 3;
        vector<int> a(size);
        for (int i = 0; i < size; i++)
            a[i] = rand() % 20 - 10;
        if (get_max_sum(a) != get_max_sum_dummy(a))
            ok = false;
    
    for (int test = 0; test < 10000; test++) 
        int size = rand() % 20 + 3;
        vector<int> a(size);
        for (int i = 0; i < size; i++)
            a[i] = rand() % 20;
        if (get_max_sum(a) != get_max_sum_dummy(a))
            ok = false;
    
    return ok;

实际解决方案是get_max_sum 函数(另外两个是蛮力解决方案和一个测试器函数,它生成一个随机数组并比较蛮力和实际解决方案的输出,我仅将它们用于测试目的)。

我的解决方案背后的想法是计算子数组中的最大总和,该子数组从i 之前的某处开始并以i - 1 结束,然后就足够了(分别为best_pref[i]best_suf[i] )。之后,我只遍历所有i 并返回best_pref[i] + best_suf[i] 的最佳值。它可以正常工作,因为best_pref[y] 为固定y 找到最佳xbest_suf[y] 为固定y 找到最佳z,并检查y 的所有可能值。

【讨论】:

@LukaRahne 正如我在回答中所说,实际的解决方案是 get_max_sum 函数(它显然具有 O(n) 时间和空间复杂度)。蛮力算法仅用于测试目的。【参考方案3】:
def solution(A):
    n = len(A)
    K1 = [0] * n
    K2 = [0] * n
    for i in range(1,n-1,1):
        K1[i] = max(K1[i-1] + A[i], 0)

    for i in range(n-2,0,-1):
        K2[i] = max(K2[i+1]+A[i], 0)

    maximum = 0;
    for i in range(1,n-1,1):
        maximum = max(maximum, K1[i-1]+K2[i+1])

    return maximum

def main():
    A = [3,2,6,-1,4,5,-1,2]
    print(solution(A))

if __name__ == '__main__': main()

【讨论】:

【参考方案4】:

红宝石 100%

def solution(a)
  max_starting =(a.length - 2).downto(0).each.inject([[],0]) do |(acc,max), i|
    [acc, acc[i]= [0, a[i] + max].max ]
  end.first

  max_ending =1.upto(a.length - 3).each.inject([[],0]) do |(acc,max), i|
    [acc, acc[i]= [0, a[i] + max].max ]
  end.first

  max_ending.each_with_index.inject(0) do |acc, (el,i)|
    [acc, el.to_i + max_starting[i+2].to_i].max
  end
end

【讨论】:

以上是关于MaxDoubleSliceSum Codility 算法的主要内容,如果未能解决你的问题,请参考以下文章

Codility---MaxDoubleSliceSum

MaxDoubleSliceSum Codility 算法