从数组中找到元素的最小绝对和

Posted

技术标签:

【中文标题】从数组中找到元素的最小绝对和【英文标题】:find the lowest absolute sum of elements from an array 【发布时间】:2018-07-15 07:05:59 【问题描述】:

我试图从下面提供的 Codility 中的问题中理解解决方案,

对于给定的包含 N 个整数的数组 A 和来自集合 −1, 1 的包含 N 个整数的序列 S,我们将 val(A, S) 定义如下:

val(A, S) = |sum A[i]*S[i] for i = 0..N−1 |

(假设零元素之和为零。)

对于给定的数组 A,我们正在寻找这样一个序列 S,它使 val(A,S) 最小化。

写一个函数:

class Solution  public int solution(int[] A); 

给定一个包含 N 个整数的数组 A,从集合 −1, 1。

例如,给定数组:

  A[0] =  1
  A[1] =  5
  A[2] =  2
  A[3] = -2

你的函数应该返回 0,因为对于 S = [−1, 1, −1, 1],val(A, S) = 0,这是可能的最小值。

假设:

N is an integer within the range [0..20,000];
each element of array A is an integer within the range [−100..100].
Complexity:

预期的最坏情况时间复杂度为 O(N*max(abs(A))2); 预期的最坏情况空间复杂度为 O(N+sum(abs(A)))(不计算输入参数所需的存储空间)。

在过去的几个小时里,我找到了想要了解的解决方案。

public static int solution(int[] A) 

        int N = A.length;

        int sum = 0;
        int max = 0;

        for (int i = 0; i < N; i++) 

            A[i] = Math.abs(A[i]);
            sum += A[i];

            max = Math.max(A[i], max);
        


        int[] counts = new int[max + 1];

        for (int i = 0; i < N; i++) 
            counts[A[i]] += 1;
        

        int[] Total = new int[sum + 1];

        Arrays.fill(Total, -1);
        Total[0] = 0;

        // Segment I
        for (int i = 1; i <= max; i++) 

            if (counts[i] > 0) 

                for (int j = 0; j <= sum; j++) 

                    // j th index is zero or positive
                    if (Total[j] >= 0) 
                        Total[j] = counts[i];
                    
                    // (i-j) th index is positive
                    else if ((j - i) >= 0 && Total[j - i] > 0) 
                        Total[j] = Total[j - i] - 1;
                    
                
            
        

        int result = sum;

        // Segment II
        for (int i = 0; i < sum / 2 + 1; i++) 

            // i- th index if zero or positive
            // BODMAS_RULE = Brackets, Orders, Division, Multiplication, Addition, Subtraction
            if (Total[i] >= 0) 
                result = Math.min(result, sum - 2 * i);
            
        

        return result;
    

如果有人能解释循环的最后两段发生了什么,那将非常有帮助。我只是在几行文字中寻找一个简短的解释。

更新

我想了解为什么我们在第一段的 for 循环中使用条件。此外,为什么我们要迭代到第二个 for 循环中的sum / 2 + 1。谢谢你。

【问题讨论】:

【参考方案1】:

要对所需属性求和,您可以找到数组 A 的子集,其总和尽可能接近总和的一半。

它有点像well-knownsubset sum problem,可以通过动态编程方法使用维度对应于总和的附加数组来解决(注意数组Total)。

请注意,该问题的解决方案仅处理正数,因此您必须对其进行修改以使用负数(例如您的示例 A[3] = -2)。

此代码使数组total 的长度为sum+1,并保存数组counts 中每个值的计数。

在第一阶段,代码用所有可能总和的非否定条目填充总表。考虑简单的设定值:count (2:3; 3:2). 第一轮用值 3,2,1,0 填充条目 0,2,4,6。 第二轮更改所有非负数的三分球和 从非负条目(例如,4--&gt; 4+3 -&gt;4+6)为链构建递减序列。之后,我们有总数组[2,-1,2,1,2,1,2,1,0,1,0,-1,0],在 1,11 处有负条目(不可能的总和)。请注意,这种方法不存储信息 - 使用了哪些项目来计算所有可能的总和,因此您必须进行更多修改。

【讨论】:

好的,我试图理解提供的注释,但它仍然没有多大意义。您介意详细说明答案吗? 但是显示的代码已经使用了动态编程——它填充了总数组(第 1 段),然后找到最接近(接近一半)的填充条目(第 2 段) 你明白子集和问题等价于你的任务吗? 是的,但它仍然对我没有帮助。你为我指明了正确的方向已经很好了。如果您能花时间写一个详尽的答案,我将不胜感激。谢谢。 至少我们为什么要使用这些条件? if (Total[j] &gt;= 0) Total[j] = counts[i]; /* * (i-j) th index is positive * */ else if ((j - i) &gt;= 0 &amp;&amp; Total[j - i] &gt; 0) Total[j] = Total[j - i] - 1; 在第一段。

以上是关于从数组中找到元素的最小绝对和的主要内容,如果未能解决你的问题,请参考以下文章

最大和最小差(贪心算法)

每日一题1200. 最小绝对差

如何从数组中找到精确的(最小)元素以匹配给定的总和

数组:正整数数组分成2组使其和的差的绝对值最小

求无序数组的两两元素之差绝对值的最小值

在大小为 N 的数组的每 k 个元素中查找最小和第二小的元素