алгоритм Дейкстры - на c++?
в течение последних четырех дней я пытаюсь понять алгоритм Дейкстры. Но я не могу. У меня есть вектор точек. Из этого я создал матрицу затрат. Но я не знаю, как сделать алгоритм Дейкстры. Источники доступны в сети, но я не из компьютерных наук, поэтому я не могу их понять. Я пытаюсь сделать такую функцию
vector<int> dijkstra(costMatrix[][])
{
....
....
return vector<int>pathPointindex
}
main()
{
vector<Point> availablePoints;
costMatrix[][]=createCostMatrix();
vector<int> indexes=dijikstra(costMatrix)
for(int i=0;i<indexes.size();i++)
cout << "path points are " << availablePoints[indexes[i]] << endl;
}
Если кто-нибудь, вы можете опубликовать код. Я не ленив. Но мой проект уже пересек крайний срок. сутки назад. Теперь я потерял надежду понять логику. Теперь я просто хочу функцию. "Человек в нужде и есть ангел" .
EDIT: особая благодарность "Локи астари" за его отличный ответ
6 ответов
советую посмотреть TopCoder учебник, который имеет очень прагматичный apporach.
Вам нужно будет узнать, как работает очередь приоритетов STL и убедитесь, что у вас нет negative edge weights
в свой график.
достойная полная реализация может быть найдена здесь. Вам нужно будет добавить к нему вектор пути и реализовать RecoverPath
способ для того, чтобы получить узлы на пути от источника к приемнику. Чтобы использовать это решение, вам также необходимо преобразовать ваш adjacency matrix
to adjacency list
in следующий путь:
for (int i=0;i<nNodes;i++)
for (int j=0;j<nNodes;j++){
if (costMatrix[i][j] != NO_EDGE_VALUE){
G[i].pb(make_pair(j,costMatrix[i],[j]));
}
}
EDIT: если ваш график плотный, я бы посоветовал вам использовать Форда Беллмана алгоритм намного проще и не должен быть намного медленнее.
EDIT2: чтобы вычислить путь, вы должны добавить в заголовок
int P[MAX]; /*array with links to parents*/
for(i=0; i<=nodes; i++) P[i] = -1; /*magic unset value*/
// dijkstra
while(!Q.empty()) {
....
if(!F[v] && D[u]+w < D[v]) {
D[v] = D[u] + w;
/*By setting P[v] value we will remember what is the
previous node on path from source */
P[v] = u; // <-- added line
Q.push(pii(v, D[v]));
}
...
}
тогда вам нужно добавить метод RecoverPath (он работает только тогда, когда существует путь)
vector<int> RecoverPath(int src, int dest){
vector<int> path;
int v = dest;
while (v != src) {
path.push_back(v);
v = P[v];
}
path.push_back(src);
std::reverse(path.begin(),path.end());
return path;
}
алгоритм Дейкстры
по-английски:это алгоритм поиска кратчайшего пути из точки А в точку Б.
В вычислительных терминах мы упрощаем маршрут до графа, состоящего из узлов и Дуг. Каждый узел представляет промежуточную точку, в то время как каждая дуга соединяет два узла и имеет (неотрицательный) вес, представляющий стоимость пересечения между двумя узлами.
для реализации алгоритма необходимо два списка:
- закончил: Список (узел, стоимость), где вы рассчитали минимальную стоимость для достижения.
- работает: отсортированный список (узел,стоимость), которые были проверены.
working.addNode(Start, 0); // No cost to get to start.
for( (node, cost) = working.popHead(); node != End; (node,cost) = working.popHead())
{
// If we have already processed this node ignore it.
if (finished.find(node))
{ continue;
}
// We have just removed a node from working.
// Because it is the top of the list it is guaranteed to be the shortest route to
// the node. If there is another route to the node it must go through one of the
// other nodes in the working list which means the cost to reach it will be higher
// (because working is sorted). Thus we have found the shortest route to the node.
// As we have found the shortest route to the node save it in finished.
finished.addNode(node,cost);
// For each arc leading from this node we need to find where we can get to.
foreach(arc in node.arcs())
{
dest = arc.dest();
if (NOT (finished.find(dest)))
{
// If the node is already in finished then we don't need to worry about it
// as that will be the shortest route other wise calculate the cost and add
// this new node to the working list.
destCost = arc.cost() + cost;
working.addNode(dest,destCost); // Note. Working is sorted list
}
}
}
так что если вы думаете об этом алгоритме. Допустим, вы едете из Лондона в Манчестер.
finished = {} // empty.
working = { (London,0) }
используя следующую матрицу затрат:
L S O B N M W
(L) ondon - 50 60 100 130 - -
(S) outhampton 50 - 70 - - - -
(O) xford 60 70 - 50 - 200 -
(B) irmingham 100 - 50 - - 80 110
(N) orwich 130 - - - - - -
(M) anchester - - 200 80 - - 80
Ne(W) castle - - - 110 - 80 -
теперь вы берете Лондон из рабочего списка (как он находится во главе) и помещаете его в готовый список. Затем добавьте в рабочий список все города, непосредственно связанные с Лондоном.
finished = { (London,0) }
working = { (Southampton, 50), (Oxford, 60), (Birmingham, 100), (Norwich,130) }
рассмотрим города в рабочем наборе внешний край пузыря, который расширился из Лондона. Задача алгоритма Дейкстры - продолжать расширять пузырь, пока мы не достигнем Манчестера (без каких-либо шагов, которые мы уже предприняли). Таким образом, пузырь всегда расширяется наружу, и мы всегда расширяем ту часть пузыря, которая наименьшая.
Итак, следующий шаг-взять голову из списка и повторите. Из Саутгемптона есть только два направления. Назад в Лондон (который мы отбрасываем, поскольку он находится в готовом списке) и Оксфорд. Стоимость добраться до Оксфорда составляет 50 + Стоимость от Саутгемптона до Оксфорда (поэтому обратите внимание, что он находится в рабочем списке дважды, но не волнуйтесь, мы отбросим его позже как не оптимальный маршрут).
finished = { (London,0), (Southampton,50) }
working = { (Oxford, 60), (Birmingham, 100), (Oxford, 120), (Norwich,130) }
Итак, повторите цикл. Возглавляет список Оксфорд. Из Оксфорда мы можем отправиться в Манчестер (200), Бирмингем (50) или обратно в Лондон (60) или Саутгемптон (помните, что нам нужно добавить стоимость поездки в Оксфорд к каждой из этих затрат выше. Обратите внимание, что из Оксфорда мы могли бы поехать в Саутгемптон, но мы уже нашли самый короткий маршрут в Саутгемптон, поэтому обработка не требуется) это оставит нас с:
finished = { (London,0), (Southampton,50), (Oxford, 60) }
working = { (Birmingham, 100), (Birmingham,110), (Oxford, 120), (Norwich,130), (Manchester,200)}
обратите внимание, что у нас есть Манчестер в рабочем списке сейчас (это наш пункт назначения). Но нам нужно продолжать работать, как мы можем найти более короткий путь. Итак, теперь мы расширяемся из Бирмингема. Оттуда мы можем отправиться в Оксфорд (50), Манчестер(80), Лондон(100), Ньюкасл(110). Добавление стоимости добраться до Бирмингема в первую очередь это дает нам:
finished = { (London,0), (Southampton,50), (Oxford, 60), (Birmingham, 100) }
working = { (Birmingham,110), (Oxford, 120), (Norwich,130), {Manchester, 180), (Manchester,200), (Newcastle, 210)}
следующие два узла. Оксфорд и Бирмингем уже в готовом списке, поэтому мы можем игнорировать их. Поэтому, если нет маршрута из Норвича в Манчестер, который составляет менее 50 миль, мы достигнем Манчестера в итерации после этого.
основная идея алгоритма Дейкстры довольно проста: Предположим, у вас есть множество точек с известными кратчайшими путями к данной точке A. Тогда предположим, что мы хотим добавить новую точку C в множество. Давайте найдем, какие точки из множества связаны с точкой, которую мы хотим добавить. Пусть это будет точка B(i) Таким образом, для всех точек B(i) мы найдем сумму расстояний между A до B(i) и B(i) и C. наименьшее из этих расстояний будет минимальным между A и C.
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <limits>
#include <set>
#include <utility>
#include <algorithm>
#include <iterator>
using namespace std;
typedef int vertex_t;
typedef double weight_t;
const weight_t max_weight = numeric_limits<double>::infinity();
struct neighbor {
vertex_t target;
weight_t weight;
neighbor(vertex_t arg_target, weight_t arg_weight)
: target(arg_target), weight(arg_weight) { }
};
typedef vector<vector<neighbor> > adjacency_list_t;
// Computing the shortest pathway
void
DijkstraComputePaths(vertex_t source,
const adjacency_list_t &adjacency_list,
vector<weight_t> &min_distance,
vector<vertex_t> &previous)
{
int n = adjacency_list.size();
min_distance.clear();
min_distance.resize(n, max_weight);
min_distance[source] = 0;
previous.clear();
previous.resize(n, -1);
set<pair<weight_t, vertex_t> > vertex_queue;
vertex_queue.insert(make_pair(min_distance[source], source));
while (!vertex_queue.empty())
{
weight_t dist = vertex_queue.begin()->first;
vertex_t u = vertex_queue.begin()->second;
vertex_queue.erase(vertex_queue.begin());
// Visit each edge exiting u
const vector<neighbor> &neighbors = adjacency_list[u];
for (vector<neighbor>::const_iterator neighbor_iter = neighbors.begin();
neighbor_iter != neighbors.end();
neighbor_iter++)
{
vertex_t v = neighbor_iter->target;
weight_t weight = neighbor_iter->weight;
weight_t distance_through_u = dist + weight;
if (distance_through_u < min_distance[v]) {
vertex_queue.erase(make_pair(min_distance[v], v));
min_distance[v] = distance_through_u;
previous[v] = u;
vertex_queue.insert(make_pair(min_distance[v], v));
}
}
} // while
}
реализация в C ++
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
using namespace std;
#define pb push_back
#define mp make_pair
#define MAXN 50100
#define INF 1000000000
int N, M, d[MAXN]; vector<int> G[MAXN], C[MAXN];
set< pair<int, int> > T;
void solve(void)
{
int i, j, k, val, x;
for(i = 2; i <= N; i++) d[i] = INF;
T.insert( mp(0, 1) );
while( T.size() > 0 )
{
val = (*T.begin()).first, x = (*T.begin()).second;
T.erase(*T.begin());
for(i = 0; i < G[x].size(); i++)
if(d[ G[x][i] ] > val + C[x][i] )
d[ G[x][i] ] = val + C[x][i], T.insert(mp(d[G[x][i]],G[x][i]));
}
}
int main(void)
{
freopen("dijkstra.in", "rt", stdin);
freopen("dijkstra.out", "wt", stdout);
int i, a, b, c;
scanf("%d %d\n", &N, &M);
for(i = 1; i <= M; i++)
scanf("%d %d %d\n", &a, &b, &c), G[a].pb(b), C[a].pb(c);
solve();
for(i = 2; i <= N; i++)
printf("%d ", d[i] == INF ? 0 : d[i]);
return 0;
}
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
const size_t INT_MAX = 0xFFFFFFFF; // or any other value representing infinite distance.
сначала создайте ребро структуры, содержащее индекс исходного узла, индекс целевого узла и ребро "вес"( длина ).
struct edge { size_t from; size_t to; size_t length; };
определите узел класса, содержащий ребра для соседних соседей.
class Node
{
public:
void AddNeighborEdge( edge _NeighborEdge ) { m_neighborsEdges.push_back( _NeighborEdge ); }
vector<edge>::iterator FirstNeighborEdge() { return m_neighborsEdges.begin(); }
vector<edge>::iterator LastNeighborEdge() { return m_neighborsEdges.end(); }
private:
vector<edge> m_neighborsEdges;
};
класс NeighborsDistanceUpdator будет использоваться в качестве" функтора " алгоритмом for_each для итеративного прохода и обновления минимального расстояния от текущего узла в графике до соседних соседей.
class NeighborsDistanceUpdator
{
public:
NeighborsDistanceUpdator( vector<size_t>& _min_distance_from_source, queue< size_t >& _nodes_to_visit ) : m_min_distance_from_source( _min_distance_from_source ), m_nodes_to_visit( _nodes_to_visit )
{}
void operator()( edge& _edge )
{
size_t from = _edge.from;
size_t to = _edge.to;
if ( m_min_distance_from_source[ to ] > m_min_distance_from_source[ from ] + _edge.length )
{
m_min_distance_from_source[ to ] = m_min_distance_from_source[ from ] + _edge.length;
m_nodes_to_visit.push( to );
}
}
private:
vector<size_t>& m_min_distance_from_source;
queue< size_t >& m_nodes_to_visit;
};
что касается алгоритма Дейкстры, просто запустите все узлы на графике и для каждого узла обновите минимальное расстояние от источника ( если меньше ), сохраняя соседние узлы для посещения.
size_t dijkstra( map< size_t, Node >& _graph, size_t _sourceIndex, size_t _targetIndex )
{
vector<size_t> min_distance_from_source( _graph.size(), INT_MAX );
min_distance_from_source[ _sourceIndex ] = 0;
queue< size_t > nodes_to_visit;
nodes_to_visit.push( _sourceIndex );
NeighborsDistanceUpdator neighborsDistanceUpdator( min_distance_from_source, nodes_to_visit );
while ( ! nodes_to_visit.empty() )
{
size_t currNodeIndex = nodes_to_visit.front();
if ( currNodeIndex == _targetIndex ) return min_distance_from_source[ currNodeIndex ];
nodes_to_visit.pop();
vector<edge>::iterator firstNeighborEdge= _graph[ currNodeIndex ].FirstNeighborEdge();
vector<edge>::iterator lastNeighborEdge= _graph[ currNodeIndex ].LastNeighborEdge();
for_each( firstNeighborEdge, lastNeighborEdge, neighborsDistanceUpdator );
}
return INT_MAX;
}