Предполагая, что есть хотя бы одна пара элементов, удовлетворяющих условиям, и нет умножения двух элементов в ней, переполнения, это может быть сделано во Theta(n-k)
времени и Theta(1)
пространстве в наихудшем и наилучшем случае, с чем-то вроде этого:
auto back_max = a[0];
auto back_min = a[0];
auto best = a[0]*a[k+1];
for(std::size_t i=1; i<n-(k+1); ++i) {
back_max = std::max(back_max, a[i]);
back_min = std::min(back_min, a[i]);
best = std::min(best, std::min(a[i+k+1]*back_max, a[i+k+1]*back_min));
}
return best;
Это оптимально с точки зрения асимптотической сложности в худшем случае как для времени, так и для пространства, поскольку оптимальным произведением может быть как минимум a[0]
любой из n-(k+1)
элементов на расстоянии k+1
, поэтому n-(k+1)
любой алгоритм, решающий проблему, должен быть прочитан как минимум целыми числами.
Идея алгоритма заключается в следующем:
Оптимальный продукт использует два элемента a
, предположим, что это a[r]
и a[s]
. Без ограничения общности можно предположить, что, s > r
поскольку произведение является коммутативным.
Из-за ограничения abs(s-r) > k
это означает, что s >= k+1
. Теперь s
каждый из индексов может удовлетворять этому условию, поэтому мы перебираем эти индексы. Это итерация i
в показанном коде, но k+1
для удобства она сдвинута (не имеет большого значения). Для каждой итерации нам нужно найти оптимальный продукт с i+k+1
наибольшим индексом и сравнить его с предыдущим лучшим предположением.
Все возможные индексы, i+k+1
с которыми нужно соединиться, являются меньшими или равными i
из-за требования к расстоянию. Нам также нужно было бы повторить все это, но в этом нет необходимости, потому что минимальный a[i+k+1]*a[j]
избыточный j
при фиксированном i
равен min(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))
монотонности продукта (принимая во внимание минимум как по отношению к минимуму, так и максимальный по сравнению a[j]
с учетом двух возможных признаки a[i+k+1]
или эквивалентно два возможных направления монотонности.)
Так как набор a[j]
значений, для которых мы оптимизируем здесь, просто {a[0], ..., a[i]}
, который просто увеличивается на один элемент ( a[i]
) в каждой итерации i
, мы можем просто отслеживать max(a[j])
и min(a[j])
с отдельными переменными, обновляя их, если они a[i]
больше или меньше предыдущих оптимальных значений. Это делается с помощью back_max
и back_min
в примере кода.
Первый шаг итерации ( i=0
) пропускается в цикле и вместо этого выполняется как инициализация переменных.
std::vector
? @Scheff - сортировка разрушила бы исходные «дистанционные» отношения.