以非递增顺序的n个数的成对和

时间:2011-12-19 19:39:11

标签: algorithm puzzle

我在编程interview blog.

中看到了这个问题
  

如果以非递减顺序给出n个数字的成对总和,则识别各个数字。如果总和已损坏,请打印-1

示例:

i/p: 4 5 7 10 12 13 

o/p: 1 3 4 9

提示就足够了。

6 个答案:

答案 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]必须是0B[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]位于0B[0]/2-1之间的事实一起,只为A[0]测试提供了一些可能性。

例如,A[0]有两种可能:01。如果我们使用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 + 313 = 4 + 9

  • 下一个最低输入(5)仅与第一个(1)中的一个加数不同,因此您可以通过取差(5-1)来计算其中一个加数。

答案 2 :(得分:1)

在他删除答案之前,我认为费迪南德·拜尔走在正确的轨道上。要重复他的部分方法:您有四个未知数,abcd 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 + ba + ca + db + cb + dc + d。然后可以按照费迪南德的原始计划轻松解决联立方程式。

不幸的是,有一对(a + db + 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