我在编程interview blog.
中看到了这个问题如果以非递减顺序给出
n
个数字的成对总和,则识别各个数字。如果总和已损坏,请打印-1
。
示例:
i/p: 4 5 7 10 12 13
o/p: 1 3 4 9
提示就足够了。
答案 0 :(得分:11)
让B
成为成对总和列表,B[0] <= B[1] <= ... <= B[m-1]
让A
成为我们尝试查找的原始数字列表,A[0] < A[1] < ... < A[n-1]
,其中m = n(n-1)/2
。
给定A[0]
,在多项式时间内计算A
从最小元素到最大元素构建A
。假设我们已经知道A[0]
。然后,由于B[0]
是B
中的最小元素,因此它只能出现为A[0] + A[1]
。同样,B[1]
必须等于A[0] + A[2]
。因此,如果我们知道A[0]
,我们就可以计算A[1]
和A[2]
。
B[2]
可能是A[0] + A[3]
或A[1] + A[2]
,如果没有先验知识,我们就无法知道它是哪一个。但是,如果我们知道A[0]
,我们可以如上所述计算A[1]
和A[2]
,然后从A[1] + A[2]
移除B
。然后保证下一个最小元素为A[0] + A[3]
,这样我们就可以找到A[3]
。继续这样,我们可以找到所有A
而无需回溯。算法看起来像这样:
for i from 1 to n-1 {
// REMOVE SEEN SUMS FROM B
for j from 0 to i-2 {
remove A[j]+A[i-1] from B
}
// SOLVE FOR NEXT TERM
A[i] = B[0] - A[0]
}
return A
以下是B = [4,5,7,10,12,13]
如果我们知道A[0]=1
,您的示例如何运作:
start
B = [4,5,7,10,12,13]
A[0] = 1
i=1:
B = [4,5,7,10,12,13]
A[1] = 4-1 = 3
i=2:
Remove 1+3 from B
B = [5,7,10,12,13]
A[2] = 5-1 = 4
i=3:
Remove 1+4 and 3+4 from B
B = [10,12,13]
A[3] = 10-1 = 9
end
Remove 1+9 and 3+9 and 4+9 from B
B = []
A = [1,3,4,9]
所以这一切都归结为了解A[0]
,我们可以从中计算A
的其余部分。
在多项式时间内计算A[0]
我们现在可以简单地尝试A[0]
的所有可能性。由于我们知道B[0] = A[0] + A[1]
,因此我们知道A[0]
必须是0
和B[0]/2 - 1
之间的整数。我们也知道
B[0] = A[0] + A[1]
B[1] = A[0] + A[2]
此外,还有一些索引i
2 <= i <= n-1
,
B[i] = A[1] + A[2]
为什么呢?因为可能小于A[1]+A[2]
的唯一条目的格式为A[0] + A[j]
,并且最多只有n-1
个这样的表达式。因此我们也知道
A[0] = (B[0]+B[1] - B[i])/2
某些2 <= i <= n-1
的。这与A[0]
位于0
和B[0]/2-1
之间的事实一起,只为A[0]
测试提供了一些可能性。
例如,A[0]
有两种可能:0
或1
。如果我们使用A[0]=0
尝试算法,那么会发生以下情况:
start
B = [4,5,7,10,12,13]
A[0] = 0
i=1:
B = [4,5,7,10,12,13]
A[1] = 4-0 = 4
i=2:
Remove 0+4 from B
B = [5,7,10,12,13]
A[2] = 5-0 = 5
i=3:
Remove 0+5 and 4+5 from B
B = !!! PROBLEM, THERE IS NO 9 IN B!
end
答案 1 :(得分:1)
一些提示:
输入的大小为N *(N-1)/ 2,因此您可以推断出输出的大小(即输入中的6个元素对应于输出中的4个元素)
输入的总和是输出的总和除以N - 1
(即1+3+4+9 = (4+5+7+10+12+13) / (4-1)
)
最低输入和最高输入分别是两个最低和两个最高输出的总和(即4 = 1 + 3
和13 = 4 + 9
)
下一个最低输入(5)仅与第一个(1)中的一个加数不同,因此您可以通过取差(5-1)来计算其中一个加数。
答案 2 :(得分:1)
a
,b
,c
和d
a ≤ b ≤ c ≤ d
。由此,可以形成所有总和的部分排序:
a + b ≤ a + c
a + b ≤ a + d
a + c ≤ b + c
a + d ≤ b + d
a + d ≤ c + d
b + c ≤ b + d
b + d ≤ c + d
如果这是一个总订单,那么人们就会知道六个值中的每一个a + b
,a + c
,a + d
,b + c
,b + d
和c + d
。然后可以按照费迪南德的原始计划轻松解决联立方程式。
不幸的是,有一对(a + d
,b + c
),可以按任意方式订购。但这很容易处理:假设a + d < b + c
(输入值都是不同的,所以不必担心使用≤)并尝试求解联立方程。然后假设b + c < a + d
并重复。如果两组方程都有解,那么原问题有两个答案。如果两个集都没有解,那么输出应为-1
。否则,你有(独特的)解决方案。
答案 3 :(得分:0)
PengOne恢复给定A [0]和B的方法很好,但有一种更好的方法来计算A [0]。请注意,B的两个最小元素是:
B[0] = A[0] + A[1]
B[1] = A[0] + A[2]
和
B[i] = A[1] + A[2]
对于某些人来说。
因此,
A[0] = (B[0] + B[1] - B[i]) / 2
对于某些i,我们只需要尝试O(n ^ {1/2})的可能性,因为我受O(n ^ {1/2})的限制,并看看是否有一个导致有效设置每个PengOne解决方案的剩余元素。总运行时间为O(n ^ {3/2}),其中n是输入中的数字数。
答案 4 :(得分:0)
最近我正在检查面试问题,我在@ PengOne提示找到第一个值的帮助下解决了这个问题,
因此,如果有人需要一个完整的工作解决方案: 它是用PHP:
时间复杂度:带有辅助变量的O((n *(n-2))+ 3 + n)。 空间复杂性:几乎与时间复杂相同。
<?php
function getSublistSize($length)
{
$i = 2;
$n = 0;
while ($i <= $length) {
if (is_int($length / $i)) {
if ($length == $i * ($i + 1) / 2) {
return ($i + 1);
}
}
++$i;
}
return $n;
}
function findSubstractList(array $list)
{
$length = count($list);
$n = getSublistSize($length);
$nth = $n - 1;
$substractList = [];
$substractTotal = array_sum($list) / ($length / 2); // A + B + C + D
/**
* formula : A = (list[0] + list[1] - list[nth -1]) / 2
* list[0] = A + B,
* list[1] = A + C,
* list[nth - 1] = B + C
*
* => ((A + B) + (A + C) - (B + C)) / 2
* => (A + A + (B + C - B - C)) / 2
* => (2A + 0) / 2 => 2A / 2
* => A
*/
$substractList[] = (($list[0] + $list[1]) - $list[$nth]) / 2;
for ($i = 0; $i < $nth; ++$i) {
$substractList[] = ($list[$i] - $substractList[0]);
}
// $substractList[3] = $substractTotal - ($list[$nth - 1] + $substractList[0]);
return $substractList;
}
$list = [5, 8, 14, 28, 40, 11, 17, 31, 43, 20, 34, 46, 40, 52, 66];
print_r(findSubstractList($list));
/**
* P ) [6, 11, 101, 15, 105, 110];
* S ) [1, 5, 10, 100]
*
* P ) [5, 8, 14, 28, 40, 11, 17, 31, 43, 20, 34, 46, 40, 52, 66];
* S ) [1, 4, 7, 13, 27, 39]
*
*/
答案 5 :(得分:-2)
我不确定最快的算法,但我可以解释一下这是如何工作的。
o / p的第一个数字是第一个和第二个i / p
之间的差异5-4=1
,所以现在你有第一个o / p号码。
第二个o / p数是第一个i / p减去第一个o / p。
4-1=3
o p / p的第三个是第二个o / p减去第一个i / p
5-1=4