问题陈述:
有3个阵列A,B,C都填充正整数,并且所有三个阵列都具有相同的大小。
查找min(| a-b | + | b-c | + | c-a |)其中a在A中,b在B中,c在C中。
整个周末我都在研究这个问题。一位朋友告诉我,它可以在线性时间内完成。我不明白这是怎么可能的。
你会怎么做?
答案 0 :(得分:15)
好吧,我想我可以在O(n log n)中完成。如果最初对数组进行排序,我只能做O(n)。
首先,请注意您可以根据需要置换a
,b
,c
,而无需更改表达式的值。因此,x
是a
,b
,c
中最小的一个;让y
成为三者的中间人;让z
成为最大值。然后请注意表达式只等于2*(z-x)
。 (编辑:这很容易看到......按顺序排列三个数字x < y < z
后,总和只有(y-x) + (z-y) + (z-x)
等于2*(z-x)
)
因此,我们真正想做的就是找到三个数字,使得外面两个尽可能地靠近在一起,另一个数字“夹在”它们之间。
首先,在O(n log n)中对所有三个数组进行排序。维护每个数组的索引;称呼这些i
,j
和k
。将所有三个初始化为零。无论哪个指数指向最小值,都要增加该指数。也就是说,如果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]
是a
,b
,c
形式实际答案;即,他们有最低|a-b|+|b-c|+|c-a|
。进一步假设a
&gt; b
&gt; c
;其他案例的证据是对称的。
引理:在我们游行期间,我们不会在j
之前增加J
,直到我们将k
增加到K
之后。证明:我们总是递增最小元素的索引,并在k <= K
,B[J] > C[k]
时递增。因此,当j=J
和k <= K
,B[j]
不是最小元素时,我们不会增加j
。
现在假设我们在k
到达K
之前将i
增加到I
之后。在我们执行该增量之前,事情会是什么样子?那么,C[k]
是当时三个中最小的一个,因为我们将要增加k
。 A[i]
小于或等于A[I]
,因为i < I
和A
已排序。最后,j <= J
因k <= K
(我们的引理),所以B[j]
也小于A[I]
。总而言之,这意味着我们此时的abs-diff-diff与<{>}相比 ,这是一个矛盾。
因此,在2*(c-a)
到达k
之前,我们不会将K
增加到i
之后。因此,在我们行军I
和i=I
期间的某个时刻。根据我们的引理,此时k=K
小于或等于j
。所以在这一点上,J
小于其他两个,B[j]
将增加;或j
介于其他两个之间,我们的总和只是B[j]
,这是正确答案。
这个证据很草率;特别是,它未能明确说明2*(A[i]-C[k])
,a
,b
中的一个或多个相等的情况。但我认为细节很容易解决。
答案 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
一遍又一遍地测试,直到我看到一些模式出现。我在这里找到的模式是预期的:每组中最接近的数字,无论数字是“高”还是“低”,都是产生最小最小和的数字。所以它成为最接近数字的问题。无论价值多少,可能都不多。