Проблема перелета в одну сторону - PullRequest
54 голосов
/ 07 июня 2010

Вы совершаете рейс в одну сторону с непрямым рейсом, который включает миллиарды неизвестное очень большое количество переводов.

  • Вы не останавливаетесь дважды в одном и том же аэропорту.
  • У вас есть 1 билет на каждую часть вашего путешествия.
  • Каждый билет содержит src и dst airport.
  • Все ваши билеты отсортированы случайным образом.
  • Вы забыли исходный аэропорт вылета (самый первый рейс) и пункт назначения (последний день).

Разработайте алгоритм, чтобы реконструировать вашу поездку с минимальной большой - O сложностью.


Пытаясь решить эту проблему, я начал использовать симметричную разницу из двух наборов, Srcs и Dsts:

1) Сортировать все ключи src в массиве Srcs
2) Сортировка всех ключей dst в массиве Dsts
3) Создайте объединенный набор из обоих массивов, чтобы найти неповторяющиеся объекты - это ваш первый src и последний dst
4) Теперь, имея начальную точку, пройдитесь по обоим массивам, используя двоичный поиск.

Но я полагаю, должен быть другой, более эффективный метод.

Ответы [ 18 ]

0 голосов
/ 24 июня 2010

Давайте на мгновение забудем структуры данных и графики.

Сначала я должен отметить, что все предположили, что циклов нет.Если маршрут проходит через один аэропорт дважды, то это намного большая проблема.


Но давайте пока оставим предположение.

Входные данные на самом деле уже упорядочены.Каждый билет является элементом отношения, который вводит порядок в набор аэропортов.(Английский не мой родной язык, поэтому это могут быть неверные математические термины)

Каждый билет содержит такую ​​информацию: airportX < airportY, поэтому при выполнении одного прохода через билеты алгоритм может воссоздать упорядоченный список, начинаяиз любого аэропорта.


Теперь давайте отбросим «линейное предположение».Никакое отношение порядка не может быть определено из такого рода вещей.Входные данные должны рассматриваться как производственные правила для формальной грамматики, где набор слов грамматики - это набор имен аэропортов.Билет, подобный этому:

src: A
dst: B

- это фактически пара постановок:

A->AB
B->AB

, из которых вы можете оставить только одну.

Теперь вам нужно сгенерироватькаждое возможное предложение, но вы можете использовать каждое производственное правило один раз.Самое длинное предложение, которое использует каждое его производство только один раз, является правильным решением.

0 голосов
/ 24 июня 2010

Обратите внимание, что если бы задача была только для определения аэропортов отправителя и пункта назначения (вместо реконструкции всей поездки), загадка, вероятно, стала бы более интересной.

А именно, при условии, что коды аэропортов заданы в виде целых чисел, аэропорты источника и назначения могут быть определены с использованием O (1) пропусков данных и O (1) дополнительной памяти (т. Е. Без использования хеш-таблиц, сортировки, двоичного поиска, и тому подобное).

Конечно, как только вы найдете источник, это также становится тривиальным вопросом для индексации и прохождения полного маршрута, но с этого момента в целом все равно потребуется как минимум O (n) дополнительной памяти (если вы не можете отсортировать данные на месте, что, кстати, позволяет решить исходную задачу за O (n log n) времени с O (1) дополнительной памятью)

0 голосов
/ 23 июня 2010

Мне кажется, что здесь основан подход на основе графов.

Каждый аэропорт - это узел, каждый билет - это преимущество. Давайте пока сделаем все ребра ненаправленными.

На первом этапе вы строите график: для каждого тикета вы ищете источник и пункт назначения и строите грань между ними.

Теперь, когда граф построен, мы знаем, что он ациклический и что через него проходит один путь. В конце концов, у вас есть билеты только на те поездки, которые вы совершили, и вы никогда не посещали один и тот же аэропорт ни разу.

На втором этапе вы ищите график: выберите любой узел и начинайте поиск в обоих направлениях, пока не найдете, что не можете продолжить. Это ваш источник и пункт назначения.

Если вам нужно конкретно указать, какой был источник, а какой - место назначения, добавьте свойство каталога к каждому ребру (но оставьте его неориентированным графом). Как только у вас есть кандидат в источник и пункт назначения, вы можете сказать, какой из них является основанным на ребре, связанном с ними.

Сложность этого алгоритма будет зависеть от времени, которое требуется для поиска конкретного узла. Если бы вы могли достичь O (1), то время должно быть линейным. У вас есть n заявок, поэтому для построения графика вам потребуется O (N) шагов, а затем O (N) для поиска и O (N) для восстановления пути. Все еще O (N). Матрица смежности даст вам это.

Если вы не можете сэкономить место, вы можете сделать хеш для узлов, который даст вам O (1) при оптимальном хешировании и всё это дерьмо.

0 голосов
/ 23 июня 2010

Если вы предполагаете объединяемую структуру списка, которая может хранить все (возможно, на диске):

  1. Создание 2 пустых хеш-таблиц S и D
  2. захватить первый элемент
  3. найдите свой источник в D
  4. Если найдено, удалите связанный узел из D и свяжите его с текущим узлом
  5. Если не найден, вставьте узел в S с помощью src
  6. повторить из 3 в другую сторону src <-> des, S <-> D
  7. повторить от 2 со следующим узлом.

O(n) время. Что касается пространства, парадокс дня рождения (или что-то очень похожее) будет держать ваш набор данных намного меньше, чем полный набор. В случае неудачи, когда он все еще становится большим (наихудший случай - O(n)), вы можете исключить случайные прогоны из хеш-таблицы и вставить их в конец очереди обработки. Ваша скорость может достигать максимума, но до тех пор, пока вы можете намного превысить порог ожидания ожидаемых столкновений (~ O(sqrt(n))), вы должны ожидать, что ваш набор данных (таблицы и входная очередь вместе) регулярно сокращается.

0 голосов
/ 07 июня 2010

Самый простой способ - использовать хеш-таблицы, но это не самая лучшая сложность в худшем случае ( O (n 2 ) )

Вместо этого:

  1. Создать группу узлов, содержащих (src, dst) O (n)
  2. Добавить узлы в список и отсортировать по src O (n log n)
  3. Для каждого (конечного) узла найдите в списке соответствующий (исходный) узел O (n log n)
  4. Найти начальный узел (например, используя топологическую сортировку или пометить узлы на шаге 3) O (n)

Всего: O (n log n)

(Для обоих алгоритмов мы предполагаем, что длина строк незначительна, т. Е. Сравнение равно O (1))

0 голосов
/ 17 июля 2011

Я написал небольшую программу на Python, использующую две хеш-таблицы, одну для подсчета и другую для отображения src в dst.Сложность зависит от реализации словаря.если словарь имеет O (1), то сложность равна O (n), если словарь имеет O (lg (n)), как в карте STL, то сложность составляет O (n lg (n))

import random
# actual journey: a-> b -> c -> ... g -> h
journey = [('a','b'), ('b','c'), ('c','d'), ('d','e'), ('e','f'), ('f','g'), ('g','h')]
#shuffle the journey.
random.shuffle(journey)
print("shffled journey : ", journey )
# Hashmap to get the count of each place
map_count = {}
# Hashmap to find the route, contains src to dst mapping
map_route = {}

# fill the hashtable
for j in journey:
    source = j[0]; dest = j[1]
    map_route[source] = dest
    i = map_count.get(source, 0)
    map_count[ source ] = i+1
    i = map_count.get(dest, 0)
    map_count[ dest ] = i+1

start = ''
# find the start point: the map entry with count = 1 and 
# key exists in map_route.
for (key,val) in map_count.items():
    if map_count[key] == 1 and map_route.has_key(key):
        start = key
        break

print("journey started at : %s" % start)
route = [] # the route
n = len(journey)  # number of cities.
while n:
    route.append( (start, map_route[start]) )
    start = map_route[start]
    n -= 1

print(" Route : " , route )
0 голосов
/ 08 января 2019

Я приведу здесь более общее решение проблемы:

Вы можете остановиться несколько раз в одном и том же аэропорту, но вы должны использовать каждый билет ровно 1 раз

Вы можете иметь более 1 билета на каждую часть поездки.

Каждый билет содержит SRC и DST аэропорта.

Все ваши билеты отсортированы случайным образом.

Вы забыли исходный аэропорт отправления (самый первый рейс) и пункт назначения (последний день).

Мой метод возвращает список городов (вектор), которые содержат все указанные города, если такая цепочка существует, и пустой список в противном случае. Когда есть несколько способов путешествовать по городам, метод возвращает наименьший лексикографический список.

#include<vector>
#include<string>
#include<unordered_map>
#include<unordered_set>
#include<set>
#include<map>

using namespace std;

struct StringPairHash
{
    size_t operator()(const pair<string, string> &p) const {
        return hash<string>()(p.first) ^ hash<string>()(p.second);
    }
};

void calcItineraryRec(const multimap<string, string> &cities, string start,
    vector<string> &itinerary, vector<string> &res,
    unordered_set<pair<string, string>, StringPairHash> &visited, bool &found)
{
    if (visited.size() == cities.size()) {
        found = true;
        res = itinerary;
        return;
    }
    if (!found) {
        auto pos = cities.equal_range(start);
        for (auto p = pos.first; p != pos.second; ++p) {
            if (visited.find({ *p }) == visited.end()) {
                visited.insert({ *p });
                itinerary.push_back(p->second);
                calcItineraryRec(cities, p->second, itinerary, res, visited, found);
                itinerary.pop_back();
                visited.erase({ *p });
            }
        }
    }
}

vector<string> calcItinerary(vector<pair<string, string>> &citiesPairs)
{
    if (citiesPairs.size() < 1)
        return {};

    multimap<string, string> cities;
    set<string> uniqueCities;
    for (auto entry : citiesPairs) {
        cities.insert({ entry });
        uniqueCities.insert(entry.first);
        uniqueCities.insert(entry.second);
    }

    for (const auto &startCity : uniqueCities) {
        vector<string> itinerary;
        itinerary.push_back(startCity);
        unordered_set<pair<string, string>, StringPairHash> visited;
        bool found = false;
        vector<string> res;
        calcItineraryRec(cities, startCity, itinerary, res, visited, found);
        if (res.size() - 1 == cities.size())
            return res;
    }
    return {};
}

Вот пример использования:

    int main()
    {
        vector<pair<string, string>> cities = { {"Y", "Z"}, {"W", "X"}, {"X", "Y"}, {"Y", "W"}, {"W", "Y"}};
        vector<string> itinerary = calcItinerary(cities); // { "W", "X", "Y", "W", "Y", "Z" }
        // another route is possible {W Y W X Y Z}, but the route above is lexicographically smaller.

        cities = { {"Y", "Z"}, {"W", "X"}, {"X", "Y"}, {"W", "Y"} };
        itinerary = calcItinerary(cities);  // empty, no way to travel all cities using each ticket exactly one time
    }
0 голосов
/ 23 июня 2010

Нет необходимости в хэше или что-то подобное. Реальный размер ввода здесь не обязательно количество билетов (скажем, n ), но общий «размер» (скажем, N ) билетов, общее количество char нужно их кодировать.

Если у нас есть алфавит из k символов (здесь k примерно 42), мы можем использовать методы bucketsort для сортировки массива n строк общий размер N , которые закодированы алфавитом из k символов за O (n + N + k) времени. Следующее работает, если n <= N </em> (тривиально) и k <= N </em> (ну N - это миллиарды, не так ли)

  1. В порядке выдачи билетов извлеките все коды аэропортов из билетов и сохраните их в struct, в котором код является строкой, а индекс билета - числом.
  2. Buckets сортировать этот массив struct с в соответствии с их кодом
  3. Запустите через этот отсортированный массив и присвойте порядковый номер (начиная с 0 ) для каждого вновь обнаруженного кода авиакомпании. Для всех элементов с одинаковым кодом (они являются последовательными) перейдите к билету (мы сохранили номер с кодом) и измените код (выберите право, src или dst) билета на порядковый номер .
  4. Во время этого прогона массива мы можем определить исходный источник src0.
  5. Теперь для всех билетов src и dst переписаны как порядковые номера, и билеты можно интерпретировать как один список, начинающийся с src0.
  6. Сделайте ранжирование списка (= топологическая сортировка с отслеживанием расстояния от src0) на билетах.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...