Попарные суммы n чисел в возрастающем порядке

Я видел этот вопрос в программировании блог интервью.

Если попарно суммы n числа задаются в неубывающем порядке, идентифицируют отдельные числа. Если сумма повреждена print -1.

пример:

i/p: 4 5 7 10 12 13 

o/p: 1 3 4 9

намека было бы достаточно.

6 ответов


пусть 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]. Поэтому, если мы знаем!--11-->, мы можем вычислить A[1] и A[2].

после этого, однако, этот шаблон ломается. B[2] может быть A[0] + A[3] или , мы можем вычислить 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] если мы знаем!--35-->:

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]. Поскольку мы знаем!--40-->, мы знаем!--11--> должно быть целое число между 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

несколько советов:

  • размер входного сигнала N*(N-1)/2, поэтому вы можете вывести размер выходного сигнала (т. е. 6 элементов на входе соответствуют 4 элементам на выходе)

  • сумма входных данных-это сумма выходных данных, деленная на N - 1 (т. е. 1+3+4+9 = (4+5+7+10+12+13) / (4-1))

  • самый низкий входной сигнал и самые высокие входные сигналы сумма 2 самых низких и 2 самых высоких выхода соответственно (т. е. 4 = 1 + 3 и 13 = 4 + 9)

  • следующий самый низкий вход (5) отличается только одним добавлением от первого (1), поэтому вы можете вычислить одно из добавлений, взяв разницу (5-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. В противном случае у вас есть (уникальное) решение.


подход Пенгоне к восстановлению данных 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}), так как i ограничен O(n^{1/2}), и посмотреть, приводит ли он к допустимой настройке оставшихся элементов решения пенгона. Общее время является O (n^{3/2}), где n-количество чисел на входе.


недавно я проверял вопросы интервью, и я решил проблему с помощью подсказки @PengOne для поиска первого значения,

поэтому, если кому-то нужно полное рабочее решение : Это в PHP :

сложность времени: O ((n * (n-2)) + 3 + n) с вспомогательными переменными. пространство сложность : почти то же самое с complextiy время.

<?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]
 *
*/

Я не уверен в самом быстром алгоритме, но я могу объяснить, как это работает.

первое число o/p, является разницей между первым и вторым i / p

5-4=1

, Так что теперь у вас есть первый o/p номер.

второе число o / p-это первый i / p минус первый o/p.

4-1=3

треть o / p-вторая o / p минус первая i / p

5-1=4