Я не знаком с Java, но поскольку в основном это всего лишь алгоритм, достаточно псевдокода:
Input:
Non-empty lists A, B, C: containing positive number(s).
Pseudo-code:
type-define tuple3 = (iterator, iterator, iterator);
function double value(tuple3 x) {
return x.elm[0].value() * x.elm[1].value() * x.elm[2].value();
}
function boolean greater_than (tuple3 x, tuple3 y) {
return (value(x) > value(y));
}
function void main() {
iterator a = A.first();
iterator b = B.first();
iterator c = C.first();
set<tuple3> Visit;
PriorityQueue<tuple3, greater_than> Q;
Q.add((a,b,c));
Visit.add((a,b,c));
while (!Q.empty()) {
tuple x = Q.pop_top();
output(x);
(a, b, c) = x;
if (a.next() != null && !Visit.contains((a.next(), b, c))) {
Q.add((a.next(), b, c));
Visit.add((a.next(), b, c));
}
if (b.next() != null && !Visit.contains((a, b.next(), c))) {
Q.add((a, b.next(), c));
Visit.add((a, b.next(), c));
}
if (c.next() != null && !Visit.contains((a, b, c.next()))) {
Q.add((a, b, c.next()));
Visit.add((a, b, c.next()));
}
}
}
Обратите внимание, что функция output()
выводит строку вывода. Я на самом деле не занимаюсь индексной печатью, но это должно быть довольно легко, верно? (Например, просто следите за индексами, увеличивая 3-tuple
до 6-tuple
, чтобы удерживать индексы дополнительными 3 элементами.) Должно быть легко распространить этот алгоритм на проблемы с числом списков больше 3.
ОБНОВЛЕНИЕ
Фактически, мы можем доказать, что в худшем случае O (N ^ 2) хранилище необходимо, если мы хотим оптимизировать скорость. Поскольку O (граница исследования) = O (N ^ 2), наше использование хранилища по крайней мере на некоторый постоянный фактор больше оптимального решения.
Не для того, чтобы предоставить официальное доказательство, но я хочу объяснить это в2D, то есть 2 списка умножения, а не 3. Тогда объяснение легко расширить.
Предположим, у нас есть список A, B с N положительными числами, отсортированными по убыванию. Эти результаты умножения NxN расположены в двумерном массиве. Например, когда N = 4, это выглядит так:
o > o > o > *
v v v v
o > o > * > o
v v v v
o > * > o > o
v v v v
* > o > o > o
Каждый o
или *
представляет результат умножения. >
означает «больше чем».
Верхний левый o
представляет A[0] * B[0]
. Каждый шаг вправо означает использование +1 индекса для A[]
, а каждый шаг вниз означает использование +1 индекса для B[]
. Для того же столбца индекс A
такой же. Для той же строки индекс B
совпадает.
Рассмотрим *
: мы знаем только, что A[]
и B[]
отсортированы по убыванию. Но мы не знаем, насколько «спускается» каждый шаг. Таким образом, эти *
могут быть в любом порядке! Любой из тех 4! заказы. Если вы по крайней мере не сохраните их в какой-то предварительно упорядоченной структуре (куча, очередь с приоритетами и т. Д.), Нам придется читать и сравнивать ее снова и снова (т.е. сортировать эти 4 продукта), что побеждает скорость оптимизациидопущение.
Таким образом, мы уже объяснили, почему требуется хранилище N.
Теперь нам нужно доказать, что нашему алгоритму 2D-версии (то есть продукту из 2 списков) требуется не более 2N хранилища.
Я просто хочу дать подсказку. Полное доказательство будет слишком длинным. Например, если в середине нашего алгоритма приоритетная очередь хранит 4 *
. Предполагая, что один из *
посещен, и два из них вставляются в очередь следующим образом:
o > o > o > *
v v v v
o > o > P > N
v v v v
o > * > N > o
v v v v
* > o > o > o
, где P
означает предыдущий, то есть самое высокое значение, выбрасываемое из очереди,и N
означает следующие два, сгенерированные +1 к каждому индексу, смежному с P
. Понятно, что эти два N
не могут быть выбраны в качестве наивысшего значения (поскольку один из *
имеет большее произведение, чем каждый из них). Пока эти более высокие не появятся в очереди, эти N
не смогут генерировать новые в очередь. Теперь, по крайней мере, два *
направления "Прогресс" заблокированы! Это означает, что когда выбрано одно из двух значений (т. Е. Самое высокое значение для всплывающего окна), оно может создать только один новый продукт в очереди. Затем очередь поддерживается размером не более 2N.
Применяя это к 3D, мы знаем, что хранилище должно быть O (N ^ 2).
ОБНОВЛЕНИЕ хранилищеиспользование для "set" реализации
Кто-то может спросить, а как насчет "set"? Набор обычно реализуется в виде хеш-таблицы, пропорциональной количеству используемых записей. Наивной реализации может потребоваться хранить все продукты (т.е. O (N ^ 2) для 2D-версии и O (N ^ 3) для 3D-версии). Тщательная настройка для удаления записей, которые никогда не были нужны, уменьшит потребность в хранилище. Рассмотрим любой продукт в 2D-версии, может быть достигнуто только не более 2 других продуктов. то есть количество тестов Set.Contains () выполняется не более двух раз для каждого продукта. Если мы будем вести подсчет и удаляем эти неиспользуемые хеш-записи, они будут действительно очень близки к этим «нужным» записям к этим продуктам в нашей очереди. Это означает, что в 2D-версии в хэш-таблице также используется O (N) хранилище, а O (N ^ 2) для 3D-версии.