データ構造/グラフ理論:最小パス問題
1.問題の説明と分類
最短経路問題(短経路問題)は、ネットワークの各エッジに数値(重み)値(長さ、コスト、時間など)がある場合、2つのノード間の合計重みと最小経路を見つけることは、最短経路の問題。
●最短経路問題の分類:との最短経路問題
既知のグローバル情報を最短経路問題
既知のローカル情報
●最短経路問題の分類:
最短パス
重み付けされていないグラフは、重み付きグラフの最短経路が
含ま加重グラフネガティブエッジとを最短経路
:最短経路問題の分類に
単一始点最短経路
グローバル最短パス
二点最短経路
2、アルゴリズムの実装
1.ダイクストラアルゴリズム
解決する問題のタイプ:単一ソースの最短パスの問題
アルゴリズムのアイデア:最短パスのサブパスは依然として最短パスです。最短経路を見つけるために、アルゴリズムは最短経路の長さを増やし、連続的に最短経路を生成します。
(1)最短経路長の最短経路の特性:
この経路には1つの弧のみが含まれている必要があり、この弧の重みは最小です。
(2)次に最短のパス長を持つ最短パスの特性
:ソースポイントからポイントへ(アークが1つだけ)、またはソースポイントから頂点v1を経由して到着するまでの2つの状況のみが可能です。頂点(2つの円弧で構成される)。
(3)次に短い経路長の最短経路の特性:
3つの状況が考えられます:直接ソースポイントからポイント(1つのアークのみ)、またはソースポイントから頂点V1を経由してポイントに到達頂点(2つの円弧で構成される)、またはソースポイントから頂点v2を経由して頂点まで。
(4)残りの最短パスの特性:
ソースポイントからポイント(アークが1つだけ)への直接、またはソースポイントから計算された最短パスの頂点を通る。再び頂点に到達します。
擬似コードの実装:
// Compute shortest path distances from s,
// return them in D
void Dijkstra(Graph* G,int* D,int s)
{
//对图G求从s出发到其他顶点的最短距离
int i,v,w;
for(i=0;i<G->n();i++)
{
// Do vertices
v=minVertex(G,D);// 取目前未访问的距离最短的顶点
if(D[v]==INFINITY) return;//不可达顶点
G->setMark(v,VISITED);//加入S集合
for (w=G->first(v);w<G->n();w= G->next(v,w))
if(D[w]>(D[v]+G->weight(v,w)))
D[w]=D[v]+G->weight(v,w);//若从源至W的路径经过V的距离更短,则更新最短距离
}
}
minVertex関数:
// Find min cost vertex寻找未访问的最短距离顶点
int minVertex(Graph* G,int* D)
{
int i,V;
// Set v to an unvisited vertex
for(i=0;i<G->n();i++)
if(G->getMark(i)==UNVISITED)
{
v=i;break;}
// Now find smallest D va lue
for(i++;i<G->n0;i++)
if((G->getMark(i)==UNVISITED)&&(i<D[v1]))
v=i;
return V;
}
優先キューによって実装されるアルゴリズム:
void Dijkstra(Graph* G, int* D, ints)
{
int i, v, w;
// v is current vertex
DijkElem temp;
DijkElem E[G->e0]; // Heap array
temp.distance=0;
temp.vertex=s;
E[0]=temp; // Initialize heap array
minheap<DijkElem,DDComp> H(E,1,G->e0);
for(i=0;i<G->n();i++)
{
// Get distances
do
{
if(H.size()==0) return;//无数据可删除
temp=H.removefirst();//取出第一一个元素,即最短距离
V=temp.vertex;
} while(G->getMark(v)==VISITED);
G->setMark(v,VISITED);
if(D[v]==INFINITY) return;//不可达顶点
for(w=G->first(V);w<G->n();w=G->next(v,w))
if (D[w]>(D[v]+G->weight(v, w))
{
// 更新D
D[w]=D[v]+G->weight(v,w);
temp.distance=D[w];
temp.vertex=w;
H.insert(temp); // Insert in heap
}
}
注:Djkstraアルゴリズムは、負でない重みを持つグラフにのみ適しています。
2. Bellman-Fordアルゴリズム
解決する問題のタイプ:単一ソースの最短経路問題
アルゴリズムのアイデア:最短経路長配列シーケンスdist1 [u]、dist2 [u]、...、dist n-1 [u]を作成します。このうち、dist1 [u]は、ソースポイントvから終点uまでの1つのエッジのみを通る最短経路の長さです。dist1[u] = Edge [v] [u];エッジから終点u、dist3 [u]までの最短経路の長さは、ソースポイントvから最大3つのエッジを通って、負の長さのエッジループを形成しない最短経路の長さであり、終点u、...、dist n-1 [u ]は、ソースポイントvから始まり、最大でn-1エッジを経由して、エンドポイントuまで負の長さのサイドループを形成しない最短パスの長さです。
distk1 [i]、j = 0、1、... n-1が計算されたとすると、これは、ソースポイントvからk-1エッジを経由して、負の長さのサイドループを形成せずにエンドポイントjまでの最短パスの長さです。
グラフの隣接行列から、各頂点jから頂点uまでの距離Edge [j] [u]を見つけ、min {dist k-1 [j] + Edge [j] [u]}を計算できます。ソースポイントvから取得できます。各頂点をバイパスして、最大で負の長さのエッジループを構成しないk個のエッジを介して終点uへの最短経路の長さ。
これを使用してdist k-1 [u]と比較し、小さい方をdist k [u]の値として使用します。
擬似コードの実装:
void Graph::BellmanFord ( const int n, const int v )
{
//在带权有向图中有的边具有负的权值。从顶点V找到所有其它顶点的最短路径
for( int i=0;i<n;i++ )
{
dist[i]=Edge[v][i];
if(i!=v&&dist[i]<MAXINT)
path[i]=V;
else path[i]=-1;
}
for(int k=2;k<n;k++)
for(int u=0;u<n;u++)
if(u!=v)
for(i=0;i<n;i++ )
if(Edge[i][u]>0 && Edge[i][u]<MAXINT && dist[u]>dist[i]+Edge[i][u])
{
dist[u]=dist[i]+Edge[i][u];
path[u]=i;
}
}
注:Bellman-Fordアルゴリズムは、負の重みを持つグラフに適しています。
3.フロイドアルゴリズム
解決する問題のタイプ:すべてのソースの最短経路問題
アルゴリズムのアイデア:
(1)k経路を頂点vからuへの任意の経路として定義し、中間の頂点番号はk未満です。0-pathはvからuへの直接のエッジです。
(2)Dk(v、u)を
vからu への最小のkパスの長さとして定義します(3)vからuへの最短のkパスパスがわかっているとすると、最短の(k + 1)パス頂点kの通過または非通過:頂点kを通過する
場合、最短(k + 1)パスの一部はvからkへの最短kパスであり、他の部分はkからuへの最短kパスです。
それ以外の場合は、その値を変更せずに最短のkパスとして保持します。
擬似コードの実装:
//Floyd's all-pairs shortest paths algorithm
void Floyd(Graph* G)
{
int D[G->n()][G->n()]; // Store distances
for(int i=0;i<G->n();i++) // Initialize
for(int j=0;j<G->n();j++)
D[i][j]=G->weight(i,j); // Compute all k paths
for(int k=0;k<G->n();k++)
for(int i=0;i<G->n();i++)
for(int j=0;j<G->n();j++)
if(D[i][j]>(D[i][k]+D[k][j]))
D[i][j]=D[i][k]+D[k][j];
}
注:フロイドアルゴリズムは、負の重みを持つグラフにも適しています。