演習は次のとおりです。
Vとwを有向グラフG =(V、E)の2つの頂点とします。線形時間アルゴリズムを設計して、vとwの間の異なる最短パス(必ずしも頂点が互いに素ではない)の数を見つけます。注:Gのエッジは重み付けされていません
この消費税について、次のように要約します。
上記の点から、私は次のことを考えています:
count
を維持しますglobal level
情報も維持したいglobal level
を1つずつ増やすと、BFSが新しいレベルに達しますshortest level
情報を維持しますglobal level
をshortest level
とcount++
に割り当てます。global level
がshortest level
に等しい限り、wに再び会うたびにcount
を増やします。global level
がshortest level
より大きくなった場合、パスではなく最短パスを探しているため、旅行を終了します。私のアルゴリズムは正しいですか? vをwに、次にwをvにすると、それはまだ線形時間と見なされますか?
これに関するいくつかのアイデアがあります。
証明:同じ頂点からx
に入る複数のパスがある場合は、明らかにx
を通る複数の方法があります。これは簡単です。ここで、x
に入る各頂点を介してx
に入る方法は1つしかないと仮定します(最大)。
Xが以前に検出された場合、現在のパスはいずれも別の最短パスに貢献できません。 xは以前に検出されているため、後に続くすべてのパスは、以前の最短パスより少なくとも1つ長くなります。したがって、これらのパスはいずれも合計に寄与できません。
ただし、これは各ノードに1回しか遭遇せずに終了することを意味します。したがって、通常のBFSは問題ありません。
これは非常に単純なアルゴリズムにコンパイルできます。これは主にBFSです。
- Mark nodes as visited as usual with BFS.
- Instead of adding just nodes to the queue in the DFS add nodes plus number of incoming paths.
- If a node that has been visited should be added ignore it.
- If you find a node again, which is currently in the queue, do not add it again, instead add the counts together.
- Propagate the counts on the queue when adding new nodes.
- when you encounter the final, the number that is stored with it, is the number of possible paths.
あなたのアルゴリズムはグラフのように壊れます
* * * 1
/ \ / \ / \ / \
v * * * w
\ / \ / \ / \ /
* * * 2
すべてのエッジが左から右に向けられています。 1
を通るパスと2
を通るパスの2つをカウントしますが、v
から8つの異なる最短パスで1
と2
の両方に到達できます、合計16個。
Qrqrqが示すように、一部のグラフではアルゴリズムが失敗しますが、BFSのアイデアは優れています。代わりに、ゼロに初期化するサイズ|V|
の配列z
を維持します。 z[i]
のi
未満の距離で、検出された頂点level
への最短パスの数を保持します。 |V|
がd
から頂点v
までの距離になるように、サイズd[i]
の配列i
も維持します。その距離がlevel
より小さい場合。 level
を0に、d[v]
を0に、z[v]
を1に初期化し(v
からv
に長さ0の単一パスがあります)、d
の他のすべてのエントリを-1
に設定します。 z
から0
へ。
これで、BFSでi
からj
へのEdgeに遭遇するたびに、次のようになります。
d[j] = -1
の場合、d[j] := level
およびz[j] := z[i]
を設定します。d[j] = level
の場合、z[j] := z[j] + z[i]
を設定します。理由は、v
からi
への最短パスごとに、v
からj
への最短パスが1つあるためです。これにより、v
から各頂点への最短パスの数が線形時間で得られます。ここで、w
から始めて同じことを繰り返します。
このアルゴリズムは私には正しいようです。
ご存知のように、BFSは線形時間(O(N)
)検索です。なぜなら、それを完了するのに必要なT
は最悪の場合_T = C + a * N
_であるためです。ここで、N
はノードの数であり、C
、a
は固定定数です。
あなたの場合、最初にv
からw
に、次にw
からv
に2回検索を実行すると、(最悪の場合)_2T
_、または_2C + 2a * N
_。これは、線形時間要件O(N)
も満たします。新しい_C' = 2C
_と新しい_a' = 2a
_を定義する場合、両方とも_C'
_および_a'
_も固定定数です。
int edgeCb( graphPT g, int x, int y )
{
if ( dist[ y ] > dist[ x ] + 1 ) {
dist[ y ] = dist[ x ] + 1; // New way
ways[ y ] = ways[ x ]; // As many ways as it's parent can be reached
} else if ( dist[ y ] == dist[ x ] + 1 ) {
ways[ y ] += ways[ x ]; // Another way
} else {
// We already found a way and that is the best
assert( dist[ y ] < g->nv );
}
return 1;
}
上記のコードは、この投稿で言及されたあらゆる種類のグラフに対して適切な結果を与えています。基本的には、BFSトラバーサル用のEdgeコールバックです。
dist [start] = 0; way [start] = 1;
残りのすべての頂点dist [x] = numberOfVertices; //これは最大の不満を超えています
BFS(g、start);
Way [end]がゼロでない場合、それはwayの数を表し、dist [end]は最短距離を表します。
Incase way [end] == 0は、最初から最後まで到達できないことを意味します。
これにループの穴があるかどうか教えてください。
BFSを変更することによる最も簡単なソリューション:
count(v)= 0、count(s)= 1. vのすべての隣接uに対して、if(d(v)+ 1 == d(u))、count(u)+ = count(v)。ここですべてをリセットし、最後の頂点から同じことを行います。
このようにできますか
レベルテーブルから、パス内の頂点までの親の数のカウントを逆方向に開始します(最初に目的の頂点になります)。
すべてのステップで、特定のレベルで見つかった個別の親の数を、目的の頂点までの最短パスに乗算します。
パスに落ちるノードのみを考慮してレベルを上げ、レベル0に達するまで各レベルで見つかったdistinct parentの数を掛けます。
これは機能しますか?
ここに記載されている適切な説明を確認してください。
https://www.geeksforgeeks.org/number-shortest-paths-unweighted-directed-graph/
簡単に言えば、任意の最短経路アルゴリズムを変更できます。更新ステップが来ると、現在の経路提案がその時点までに見つかった最短経路と同じ長さであるときに以前に発見された最短経路の数のカウンターを増やします。
特定の場合、グラフが重みなしまたはすべてのエッジの重みが一定の場合、最も簡単な方法はBFSを変更することです。