Все остальные, сравнивающие это с проблемой коммивояжера, вероятно, не внимательно прочитали ваш вопрос. В TSP цель состоит в том, чтобы найти кратчайший цикл, который посещает все вершины (гамильтонов цикл) - это соответствует тому, что каждый узел помечен как «mustpass».
В вашем случае, учитывая, что у вас всего около дюжины с пометкой «mustpass», и с учетом этого 12! довольно мала (479001600), вы можете просто попробовать все перестановки только узлов 'mustpass' и посмотреть на кратчайший путь от 'start' до 'end', который посещает узлы 'mustpass' в этом порядке - он просто быть конкатенацией кратчайших путей между каждыми двумя последовательными узлами в этом списке.
Другими словами, сначала найдите кратчайшее расстояние между каждой парой вершин (вы можете использовать алгоритм Дейкстры или другие, но с этими небольшими числами (100 узлов) даже простейший алгоритм Флойда-Уоршалла будет работать со временем). Затем, когда у вас есть это в таблице, попробуйте все перестановки ваших «mustpass» узлов и все остальное.
Что-то вроде этого:
//Precomputation: Find all pairs shortest paths, e.g. using Floyd-Warshall
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
for i=1 to n:
for j=1 to n:
d[i][j] = min(d[i][j], d[i][k] + d[k][j])
//That *really* gives the shortest distance between every pair of nodes! :-)
//Now try all permutations
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest
(Конечно, это не настоящий код, и если вам нужен реальный путь, вам нужно будет отслеживать, какая перестановка дает кратчайшее расстояние, а также каковы кратчайшие пути для всех пар, но вы поняли идею.)
Он будет работать не более чем за несколько секунд на любом подходящем языке :)
[Если у вас есть n узлов и k узлов 'mustpass', время его работы будет O (n 3 ) для части Флойда-Уоршалла и O (k! N ) для части со всеми перестановками, а 100 ^ 3 + (12!) (100) практически пустяки, если только у вас нет действительно ограничивающих ограничений.]
a
доc
другогоb
. Может случиться так, что кратчайшие пути отb
доa
иc
разделяют ребро. В этом случае ребро будет повторяться дважды. Один из двух путей должен быть хуже оптимального, чтобы не генерировать циклы.INF
, строкаd[i][j] = min(d[i][j], d[i][k] + d[k][j])
становитсяd[i][j] = min(INF, INF + INF)
равной, а все ячейки всегда остаются равнымиINF
. Вам нужно добавить шаг, чтобы заполнить этот массив длинами ребер из графа.запустите алгоритм Джикстры, чтобы найти кратчайшие пути между всеми критическими узлами (начальный, конечный и обязательный), затем обход в глубину должен указать вам кратчайший путь через получившийся подграф, который касается начала всех узлов. . mustpasses ... конец
источник
Это две проблемы ... Стивен Лоу указал на это, но не уделил должного внимания второй половине проблемы.
Сначала вы должны найти кратчайшие пути между всеми вашими критическими узлами (начало, конец, должен пройти). Как только эти пути обнаружены, вы можете построить упрощенный граф, где каждое ребро в новом графе - это путь от одного критического узла к другому в исходном графе. Есть много алгоритмов поиска пути, которые вы можете использовать, чтобы найти здесь кратчайший путь.
Однако, как только у вас есть этот новый график, у вас точно будет проблема коммивояжера (ну, почти ... не нужно возвращаться к исходной точке). Любые сообщения по этому поводу, упомянутые выше, будут применяться.
источник
Собственно, проблема, которую вы разместили, похожа на задачу коммивояжера, но я думаю, что она ближе к простой задаче поиска пути. Вместо того чтобы посещать каждый узел, вам просто нужно посетить определенный набор узлов в кратчайшие сроки (расстояние).
Причина этого в том, что, в отличие от задачи коммивояжера, кукурузный лабиринт не позволит вам перемещаться напрямую из одной точки в любую другую точку на карте без необходимости проходить через другие узлы, чтобы добраться туда.
Я бы действительно рекомендовал A * поиск пути в качестве метода для рассмотрения. Вы устанавливаете это, решая, какие узлы имеют доступ к каким другим узлам напрямую, и какова «стоимость» каждого перехода от конкретного узла. В этом случае похоже, что каждый «переход» может иметь равную стоимость, поскольку ваши узлы кажутся относительно близко расположенными. A * может использовать эту информацию, чтобы найти путь с наименьшей стоимостью между любыми двумя точками. Поскольку вам нужно добраться из точки A в точку B и посетить примерно 12 между ними, даже метод грубой силы с использованием поиска пути не повредит.
Просто альтернатива для рассмотрения. Это действительно очень похоже на задачу коммивояжера, и это хорошие статьи, которые стоит прочитать, но присмотритесь внимательнее, и вы увидите, что это только чрезмерно усложняющие вещи. ^ _ ^ Это исходит из головы программиста видеоигр, который раньше имел дело с подобными вещами.
источник
У Эндрю Топа правильная идея:
1) Алгоритм Джикстры 2) Некоторая эвристика TSP.
Я рекомендую эвристику Лин-Кернигана: это одна из самых известных проблем NP Complete. Единственное, что нужно помнить, это то, что после того, как вы снова расширили график после шага 2, у вас могут быть петли в расширенном пути, поэтому вам следует обходить их, закорачивая (посмотрите на степень вершин на вашем пути).
На самом деле я не уверен, насколько хорошо это решение будет по сравнению с оптимальным. Вероятно, есть патологические случаи, связанные с коротким замыканием. В конце концов, эта проблема ОЧЕНЬ похожа на Steiner Tree: http://en.wikipedia.org/wiki/Steiner_tree, и вы определенно не можете приблизиться к Steiner Tree, например, просто сжав свой граф и запустив Kruskal's.
источник
Это не проблема TSP и не NP-сложная, потому что исходный вопрос не требует, чтобы узлы, обязательные для прохождения, посещались только один раз. Это делает ответ намного проще простого перебора после составления списка кратчайших путей между всеми узлами, которые необходимо пройти через алгоритм Дейкстры. Может быть и лучший способ, но самый простой - просто работать с двоичным деревом в обратном направлении. Представьте себе список узлов [начало, a, b, c, конец]. Суммируйте простые расстояния [start-> a-> b-> c-> end], это ваше новое целевое расстояние, которое нужно преодолеть. Теперь попробуйте [start-> a-> c-> b-> end] и, если это лучше, установите его в качестве цели (и помните, что это произошло из этого шаблона узлов). Работайте над перестановками в обратном направлении:
Один из них будет самым коротким.
(где узлы, которые «посещались несколько раз», если таковые имеются? Они просто скрыты на этапе инициализации кратчайшего пути. Кратчайший путь между a и b может содержать c или даже конечную точку. Вам не нужно беспокоиться )
источник
Учитывая, что количество узлов и ребер относительно невелико, вы, вероятно, сможете вычислить все возможные пути и выбрать самый короткий.
Обычно это называется проблемой коммивояжера и имеет недетерминированное полиномиальное время выполнения, независимо от того, какой алгоритм вы используете.
http://en.wikipedia.org/wiki/Traveling_salesman_problem
источник
Вопрос говорит о must-pass в ЛЮБОМ порядке . Я пытался найти решение об определенном порядке узлов, которые необходимо пройти. Я нашел свой ответ, но, поскольку ни у одного вопроса о StackOverflow не было аналогичного вопроса, я публикую здесь, чтобы дать людям возможность извлечь из него пользу.
Если порядок или обязательный проход определены, вы можете запустить алгоритм Дейкстры несколько раз. Например , давайте предположим , что вы должны начать с
s
прохода черезk1
,k2
иk3
(в соответствующем порядке) и остановка наe
. Затем вы можете запустить алгоритм Дейкстры между каждой последовательной парой узлов. Стоимость и путь будет по формуле:dijkstras(s, k1) + dijkstras(k1, k2) + dijkstras(k2, k3) + dijkstras(k3, 3)
источник
Как насчет применения грубой силы к дюжине узлов, которые необходимо посетить. Вы можете достаточно легко охватить все возможные комбинации из 12 узлов, и это дает вам оптимальную схему, которой вы можете следовать, чтобы покрыть их.
Теперь ваша задача упрощена до поиска оптимальных маршрутов от начального узла до цепи, по которым вы затем следуете, пока не пройдете их, а затем находите маршрут от этого до конца.
Окончательный путь состоит из:
начало -> путь к схеме * -> схема узлов, обязательных к посещению -> путь до конца * -> конец
Вы найдете пути, отмеченные мной *, вот так
Выполните поиск A * от начального узла до каждой точки схемы, для каждого из них выполните поиск A * от следующего и предыдущего узла схемы до конца (потому что вы можете следовать по кругу в любом направлении). В итоге получается много путей поиска, и вы можете выбрать тот, который стоит меньше всего.
Есть много возможностей для оптимизации путем кеширования поисковых запросов, но я думаю, что это приведет к хорошим решениям.
Однако он даже близко не подходит к поиску оптимального решения, потому что это может включать в себя оставление цепи, которую необходимо посетить в рамках поиска.
источник
Одна вещь, о которой нигде не упоминается, это то, можно ли посещать одну и ту же вершину более одного раза на пути. Большинство ответов здесь предполагают, что можно посещать одно и то же ребро несколько раз, но я считаю, что с учетом вопроса (путь не должен посещать одну и ту же вершину более одного раза!), Что нельзя посещать одну и ту же вершину дважды.
Таким образом, подход грубой силы по-прежнему будет применяться, но вам придется удалить уже используемые вершины при попытке вычислить каждое подмножество пути.
источник