過去4日間、私はダイクストラのアルゴリズムを理解しようとしています。しかし、私はできません。私は点のベクトルを持っています。それから、コストマトリックスを作成しました。しかし、ダイクストラのアルゴリズムを作成する方法がわかりません。ソースはネットで入手できますが、私はコンピュータサイエンスのバックグラウンドを持っていないので、理解できません。このような機能を作ろうとしています
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;
}
誰かがいたら、コードを投稿してください。私は怠け者ではありません。しかし、私のプロジェクトはすでに1日前に締め切りを過ぎました。今、私は論理を理解するという私の希望を失いました。今、私はその機能が欲しいだけです。 「困っている人は確かに天使です」。
編集:「Lokiastari」の素晴らしい回答に感謝します
TopCoder チュートリアルを見て、実用的なアプローチをとることをお勧めします。 STL優先度キューがどのように機能するかを調べ、グラフにnegative Edge weights
が含まれていないことを確認する必要があります。
まともな完全な実装は見つけることができます ここ 。ソースからシンクへのパス上のノードを取得するには、Pathベクトルを追加し、RecoverPath
メソッドを実装する必要があります。このソリューションを使用するには、次の方法でadjacency matrix
をadjacency list
に変換する必要もあります。
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]));
}
}
編集:グラフが密集している場合は、使用することをお勧めします Ford Bellman アルゴリズムははるかに単純で、それほど遅くなることはありません。
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;
}
これは、ポイントAからポイントBへの最短ルートを見つけるためのアルゴリズムです。
計算用語では、ノードとアークで構成されるグラフへのルートを簡略化します。各ノードは中間点を表し、各アークは2つのノードを接続し、2つのノード間を移動するコストを表す(負ではない)重みを持ちます。
アルゴリズムを実装するには、2つのリストが必要です。
アルゴリズム:
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) }
ロンドンから拡大したバブルの外縁のワーキングセット内の町を考えてみましょう。ダイクストラのアルゴリズムの仕事は、マンチェスターに到達するまでバブルを拡大し続けることです(すでに実行した手順をたどることはありません)。したがって、バブルは常に外側に拡張し、バブルの最小部分を常に拡張します。
したがって、次のステップは、リストの先頭に立って繰り返すことです。サウサンプトンからの目的地は2つだけです。ロンドン(完成したリストにあるので破棄します)とオックスフォードに戻ります。オックスフォードに到達するためのコストは、50 +サウサンプトンからオックスフォードまでのコストです(そのため、作業リストに2回表示されていますが、最適なルートではないため後で破棄することになるのでご注意ください)。
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)}
次の2つのノード。オックスフォードとバーミンガムはすでに完成リストに含まれているので、無視してかまいません。したがって、ノリッジからマンチェスターまでのルートが50マイル未満でない限り、その後の反復でマンチェスターに到達します。
#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
}
ダイクストラアルゴリズムの主な考え方はかなり単純です。特定の点Aへの既知の最短経路を持つ点のセットがあるとします。次に、新しい点Cをセットに追加するとします。セットのどのポイントが追加したいポイントに接続されているかを見つけましょう。ポイントのB(i)とすると、すべてのポイントのB(i)について、AからB(i)までの距離の合計を見つけることができます。およびB(i)およびC。その距離の最小値は、AとCの間の最小値になります。
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; };
クラスを定義しますNode隣接する近傍へのエッジを含みます。
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;
}
テスト...
int main()
{
Node node1;
Node node2;
Node node3;
Node node4;
map< size_t, Node > graph;
Edge ed;
ed.from = 0;
ed.to = 1;
ed.length = 1;
node1.AddNeighborEdge( ed );
cout << "node: " << 0 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 0;
ed.to = 2;
ed.length = 4;
node1.AddNeighborEdge( ed );
graph.insert( make_pair( 0, node1 ) );
cout << "node: " << 0 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 1;
ed.to = 2;
ed.length = 1;
node2.AddNeighborEdge( ed );
cout << "node: " << 1 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 1;
ed.to = 3;
ed.length = 3;
node2.AddNeighborEdge( ed );
graph.insert( make_pair( 1, node2 ) );
cout << "node: " << 1 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 2;
ed.to = 3;
ed.length = 1;
node3.AddNeighborEdge( ed );
graph.insert( make_pair( 2, node3 ) );
cout << "node: " << 2 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 3;
ed.to = INT_MAX;
ed.length = INT_MAX;
node3.AddNeighborEdge( ed );
graph.insert( make_pair( 3, node4 ) );
cout << "node: " << 2 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
cout << "min length from: 1 to 4 = " << dijkstra( graph, 0,3 ) << endl;
}