找到最大的连续和,使得它的最小值和它的补码最大

Posted

技术标签:

【中文标题】找到最大的连续和,使得它的最小值和它的补码最大【英文标题】:Find largest continuous sum such that the minimum of it and it's complement is largest 【发布时间】:2014-03-04 20:16:02 【问题描述】:

我得到了一个数字序列a_1,a_2,...,a_n。它的总和是S=a_1+a_2+...+a_n,我需要找到一个子序列a_i,...,a_j,使得min(S-(a_i+...+a_j),a_i+...+a_j) 是最大可能的(两个总和都必须非空)。

例子:

1,2,3,4,5 序列是3,4,因为那是min(S-(a_i+...+a_j),a_i+...+a_j)=min(8,7)=7(它是最大可能的,可以检查其他子序列)。

我努力做到这一点。

我将所有值加载到数组tab[n]

我这样做n-1tab[i]+=tab[i-j]。所以tab[j] 是从开始到j 的总和。

我检查所有可能的总和 a_i+...+a_j=tab[j]-tab[i-1] 并从总和中减去它,取最小值,看看它是否比以前大。

需要O(n^2)。这让我非常难过和痛苦。有没有更好的办法?

【问题讨论】:

数字可以是负数吗? 不,都是正面的。 【参考方案1】:

似乎这可以在 O(n) 时间内完成。

    计算总和S。理想的子序列和是最接近S/2 的最长子序列和。 从i=j=0 开始并增加j 直到sum(a_i..a_j)sum(a_i..a_j+1) 尽可能接近S/2。请注意哪个更接近并保存 i_best,j_best,sum_best 的值。 增加i,然后再次增加j,直到sum(a_i..a_j)sum(a_i..a_j+1)尽可能接近S/2。请注意哪个更接近并替换 i_best,j_best,sum_best 的值,如果它们更好的话。重复此步骤直至完成。

请注意,ij 都不会递减,因此它们总共最多更改 O(n) 次。由于所有其他操作只需要恒定的时间,因此整个算法的运行时间为 O(n)。

【讨论】:

某些步骤可能需要 O(n) 次操作,但总时间为 O(n) :-) @Egor:我在答案中添加了这个 @NiklasB。您将其更改为“这是 O(n),因为在每个步骤中,您只增加 i 和 j” - 这不是真的;每次增加 ij 时,该算法都会采取额外的步骤,但这些额外的步骤在 O(1) 中完成。这个含义在您的编辑中丢失了。如果您的澄清仍然可以保留我的初衷,那就太好了,否则我将在今天晚些时候进行编辑。 @Matt:好吧,也许这是一个糟糕的表述。我的意思是强调“递增”,就像“你只是递增 i 和 j,而不是递减它们”。让我试着找到一个更好的配方。【参考方案2】:

让我们先做一些澄清。

    序列的subsequence 实际上是序列索引的 子集。 Haivng 说,特别是在您的序列具有不同元素的情况下,您的问题将 减少 为著名的Partition 问题,这是众所周知的 NP 完全问题。如果是这种情况,您可以设法解决 O(Sn) 中的问题,其中“n”是元素的数量,“S”是总和。这不是多项式时间,因为“S”可以任意大。 让我们考虑具有连续子序列的情况。您需要观察数组元素两次。第一次运行将它们总结为一些“S”。在第二次运行中,您仔细调整数组长度。假设您知道 a[i] + a[i + 1] + ... + a[j] > S / 2。然后让 i = i + 1 减少总和。相反,如果它更小,你会增加 j。

此代码在 O(n) 中运行。

Python 代码:

    from math import fabs
    a = [1, 2, 3, 4, 5]
    i = 0
    j = 0
    S = sum(a)
    s = 0
    while s + a[j] <= S / 2:
        s = s + a[j]    
        j = j + 1
    s = s + a[j]


    best_case = (i, j)
    best_difference = fabs(S / 2 - s)
    while True:    
        if fabs(S / 2 - s) < best_difference:
            best_case = (i, j)
            best_difference = fabs(S / 2 - s)
        if s > S / 2:
            s -= a[i]
            i += 1        
        else:
            j += 1
            if j == len(a):
                break
            s += a[j]

    print best_case
    i = best_case[0]
    j = best_case[1]
    print "Best subarray = ", a[i:j + 1]
    print "Best sum = " , sum(a[i:j + 1])

【讨论】:

OP 谈论“连续”总和和“a_i, a_i+1, ..., a_j”,所以我猜这个问题实际上是关于子字符串而不是子序列 我也猜到了。否则,“子集”一词会是更好的选择。

以上是关于找到最大的连续和,使得它的最小值和它的补码最大的主要内容,如果未能解决你的问题,请参考以下文章

查找金属纹理中的最小值和最大值

用键打印dict中的最大值[重复]

在数组中查找最小值和最大值

查找数组的最小值和最大值

C语言试题三十五之找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回。主函数中x是数组名,n 是x中的数据个数,max存放最大值,index存放最大值所在元素的下标。

C语言试题三十五之找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回。主函数中x是数组名,n 是x中的数据个数,max存放最大值,index存放最大值所在元素的下标。