我有一个我似乎无法弄清楚的面试问题。给定大小为N的阵列,找到大小为k的子集,使得子集中的元素彼此相距最远。换句话说,最大化元素之间的最小成对距离。
Example:
Array = [1,2,6,10]
k = 3
answer = [1,6,10]
强力方式需要找到大小为k的所有子集,这些子集在运行时是指数级的。
我的一个想法是从数组中均匀分布值。我的意思是
这是基于直觉,即元素应尽可能均匀地分布。我不知道如何证明它有效/无效。如果有人知道如何或有更好的算法,请分享。谢谢!
答案 0 :(得分:6)
这可以使用DP在多项式时间内解决。
如您所述,第一步是对列表A进行排序。让X [i,j]成为从第一个元素A中选择j个元素的解决方案。
现在,X [i + 1,j + 1] = max(min(X [k,j],A [i + 1] -A [k]))超过k <= i。
我将离开初始化步骤并记住子集步骤供您使用。
在您的示例(1,2,6,10)中,它的工作方式如下:
1 2 6 10
1 - - - -
2 - 1 5 9
3 - - 1 4
4 - - - 1
答案 1 :(得分:2)
我认为基本的想法是正确的。您应该首先对数组进行排序,然后获取第一个和最后一个元素,然后确定其余元素。
我想不出一个多项式算法来解决这个问题,所以我建议使用两个选项中的一个。
一个是使用搜索算法,分支绑定样式,因为你手头有一个很好的启发式:任何解决方案的上限是到目前为止所选元素之间的最小间隙,所以第一个猜测(均匀间隔的细胞,如你所建议的)可以给你一个良好的基线,这将有助于立即修剪大多数分支。这适用于k
的较小值,但最糟糕的情况是O(N^k)
。
另一种选择是从相同的基线开始,计算它的最小成对距离,然后尝试改进它。假设您有一个最小距离为10的子集,现在尝试用11来获得一个。这可以通过贪婪算法轻松完成 - 选择排序序列中的第一项,使其与前一项之间的距离更大 - 或等于你想要的距离。如果你成功了,试着进一步增加,如果你失败了 - 没有这样的子集。
当阵列很大并且k
也相对较大时,后一种解决方案可以更快,但是阵列中的元素相对较小。如果它们受到某个值M
的约束,则此算法将花费O(N*M)
时间,或者稍微改进O(N*log(M))
,其中N是数组的大小。
正如Evgeny Kluev在他的回答中所建议的那样,最大成对距离也有一个很好的上限,可用于这些算法中的任何一个。所以后者的复杂性实际上是O(N*log(M/k))
。
答案 2 :(得分:0)
我想你的套装是有序的。如果没有,我的答案会稍微改变。
Let's suppose you have an array X = (X1, X2, ..., Xn)
Energy(Xi) = min(|X(i-1) - Xi|, |X(i+1) - Xi|), 1 < i <n
j <- 1
while j < n - k do
X.Exclude(min(Energy(Xi)), 1 < i < n)
j <- j + 1
n <- n - 1
end while
答案 3 :(得分:0)
$length = length($array); sort($array); //sorts the list in ascending order $differences = ($array << 1) - $array; //gets the difference between each value and the next largest value sort($differences); //sorts the list in ascending order $max = ($array[$length-1]-$array[0])/$M; //this is the theoretical max of how large the result can be $result = array(); for ($i = 0; i < $length-1; $i++){ $count += $differences[i]; if ($length-$i == $M - 1 || $count >= $max){ //if there are either no more coins that can be taken or we have gone above or equal to the theoretical max, add a point $result.push_back($count); $count = 0; $M--; } } return min($result)
对于非代码人员:对列表进行排序,找到每个2个连续元素之间的差异,对该列表进行排序(按升序排列),然后循环遍历它总结顺序值,直到您通过理论最大值或不存在剩下足够的元素;然后将该值添加到新数组并继续,直到您到达数组的末尾。然后返回新创建的数组的最小值。
这只是一个快速草案。快速浏览一下这里的任何操作都可以在线性时间内进行(基数排序)。
例如,对于1,4,7,100和200以及M = 3,我们得到:
$differences = 3, 3, 93, 100 $max = (200-1)/3 ~ 67 then we loop: $count = 3, 3+3=6, 6+93=99 > 67 so we push 99 $count = 100 > 67 so we push 100 min(99,100) = 99
这是一个简单的练习,将其转换为我留给读者的固定解决方案(P.S.在读完书中的所有时间之后,我一直想说出来:P)