绝对值之和的最小值

时间:2011-07-04 04:45:33

标签: algorithm math sum

问题陈述:

有3个阵列A,B,C都填充正整数,并且所有三个阵列都具有相同的大小。

查找min(| a-b | + | b-c | + | c-a |)其中a在A中,b在B中,c在C中。


整个周末我都在研究这个问题。一位朋友告诉我,它可以在线性时间内完成。我不明白这是怎么可能的。

你会怎么做?

2 个答案:

答案 0 :(得分:15)

好吧,我想我可以在O(n log n)中完成。如果最初对数组进行排序,我只能做O(n)。

首先,请注意您可以根据需要置换abc,而无需更改表达式的值。因此,xabc中最小的一个;让y成为三者的中间人;让z成为最大值。然后请注意表达式只等于2*(z-x)。 (编辑:这很容易看到......按顺序排列三个数字x < y < z后,总和只有(y-x) + (z-y) + (z-x)等于2*(z-x)

因此,我们真正想做的就是找到三个数字,使得外面两个尽可能地靠近在一起,另一个数字“夹在”它们之间。

首先,在O(n log n)中对所有三个数组进行排序。维护每个数组的索引;称呼这些ijk。将所有三个初始化为零。无论哪个指数指向最小值,都要增加该指数。也就是说,如果A[i]小于B[j]C[k],则增加i;如果B[j]最小,则增加j;如果C[k]最小,则增加k。重复,一直跟踪|A[i]-B[j]| + |B[j]-C[k]| + |C[k]-A[i]|。你在这次游行中观察到的最小值是你的答案。 (当三个中最小的一个位于其数组的末尾时,请停止,因为已完成。)

在每个步骤中,您只需向一个索引添加一个;但是在结束之前,你只能为每个阵列执行n次。所以这最多是3*n步,即O(n),小于O(n log n),这意味着总时间为O(n log n)。 (或者只是O(n),如果你可以假设数组已经排序。)

草图证明这是有效的:假设A[I]B[J]C[K]abc形式实际答案;即,他们有最低|a-b|+|b-c|+|c-a|。进一步假设a&gt; b&gt; c;其他案例的证据是对称的。

引理:在我们游行期间,我们不会在j之前增加J,直到我们将k增加到K之后。证明:我们总是递增最小元素的索引,并在k <= KB[J] > C[k]时递增。因此,当j=Jk <= KB[j]不是最小元素时,我们不会增加j

现在假设我们在k到达K之前将i增加到I之后。在我们执行该增量之前,事情会是什么样子?那么,C[k]是当时三个中最小的一个,因为我们将要增加kA[i]小于或等于A[I],因为i < IA已排序。最后,j <= Jk <= K(我们的引理),所以B[j]也小于A[I]。总而言之,这意味着我们此时的abs-diff-diff与<{>}相比 ,这是一个矛盾。

因此,在2*(c-a)到达k之前,我们不会将K增加到i之后。因此,在我们行军Ii=I期间的某个时刻。根据我们的引理,此时k=K小于或等于j。所以在这一点上,J小于其他两个,B[j]将增加;或j介于其他两个之间,我们的总和只是B[j],这是正确答案。

这个证据很草率;特别是,它未能明确说明2*(A[i]-C[k])ab中的一个或多个相等的情况。但我认为细节很容易解决。

答案 1 :(得分:1)

我会写一个非常简单的程序:

#!/usr/bin/python
import sys, os, random
A = random.sample(range(100), 10)
B = random.sample(range(100), 10)
C = random.sample(range(100), 10)
minsum = sys.maxint
for a in A:
 for b in B:
  for c in C:
   print 'checking with a=%d b=%d c=%d' % (a, b, c)
   abcsum = abs(a - b) + abs(b - c) + abs(c - a)
   if abcsum < minsum:
    print 'found new low sum %d with a=%d b=%d c=%d' % (abcsum, a, b, c)
    minsum = abcsum

一遍又一遍地测试,直到我看到一些模式出现。我在这里找到的模式是预期的:每组中最接近的数字,无论数字是“高”还是“低”,都是产生最小最小和的数字。所以它成为最接近数字的问题。无论价值多少,可能都不多。