更新: OPには現在、数回の説明があり、現在は別の問題です。問題の最初のバージョンに関する私の考え(またはむしろそれについての私の理解)を文書化するために、これをここに残しておきます。問題の現在のバージョンに対して新しい答えを試してみます。
更新の終了
OPが未解決の質問のいくつかを明らかにしていないのは残念です。私は次のことを想定します:
- 重みは+/-1です。
- nは頂点の数です
最初の仮定は明らかに一般性を失うことはありませんが、(2番目の仮定を介して)nの値に大きな影響を与えます。最初の仮定がなければ、小さな(固定された)グラフでさえ、重みを無制限に変化させることにより、任意の長い解を持つことができます。
私が提案するアルゴリズムは非常に単純で、よく知られているグラフアルゴリズムに似ています。私はグラフの専門家ではないので、場所によっては間違った言葉を使うかもしれません。お気軽に訂正してください。
- ソース頂点については、コスト0を覚えておいてください。todoリストに(source、0)を追加します。
- ToDoリストからアイテムをポップします。頂点のすべての出力エッジをたどり、新しいコストcを計算して新しい頂点vに到達します。新しいコストが有効で(c>=0およびc<=n ^ 2、以下を参照)、vについて記憶されていない場合は、追加します。記憶されているvのコスト値に追加し、(v、c)をtodoリストに追加します。
- ToDoリストが空でない場合は、手順2に進みます(または、コスト0で目的地に到達できる場合は、早めに中断します)。
即時の行き止まりではない各「ステップ」が、新しい(頂点、コスト)組み合わせを作成することは明らかです。これらの組み合わせのうち最大でn*n ^ 2 = n ^ 3が格納されるため、ある意味で、このアルゴリズムはO(n ^ 3)です。
さて、なぜこれが最適なパスを見つけるのですか?私には本当の証拠はありませんが、以下の考えが私がこれで十分だと思う理由を正当化すると思います。そしてそれらが本当の証拠に変わる可能性があるかもしれません。
私たちが示さなければならないのは、条件c <= n^2で十分であることは明らかだと思います。
まず、任意の(到達可能な)頂点にn未満のコストで到達できることに注意してください。
(v、c)を最適パスの一部とし、c> n ^2とします。c>nであるため、(v、c)に到達する前に、パス上に何らかのサイクルが必要です。ここで、サイクルのコストは0<m1です。 <nであり、(v、c)に到達した後、パス上に何らかのサイクルが存在する必要があります。ここで、サイクルのコストは0>m2>-nです。
さらに、上記の最初のサイクルに接するパスによって、コスト0 <= c1 <nのソースからvに到達可能にし、コスト0 <= c2 <nのパスによって、vから宛先に到達可能にします。上記の他のサイクルに触れます。
次に、コストc1、c1 + m1、c1 + 2 * m1、...でソースからvへのパスを構築し、コストc2、c2 + m2、c2 + 2 * m2、..でvから宛先へのパスを構築できます。 。c1 + c2 + a * m1 + b * m2が最小になるように、0 <= a<=m2および0<=b <= m1を選択します。これにより、最適なパスのコストが削減されます。この最適なパスでは、vのコストはc1 + a * m1 <n^2になります。
m1とm2のgcdが1の場合、コストは0になります。gcdが1より大きい場合、gcdが1になるように他のサイクルを選択できる可能性があります。それが不可能な場合は、それも不可能です。最適なソリューションのために、そして最適なソリューションのための正のコストがあります。
(はい、この証明の試みにはいくつかの問題があります。いくつかの正または負のサイクルコストなどのgcdを取る必要があるかもしれません。しかし、反例に非常に興味があります。)
ここにいくつかの(Python)コードがあります:
def f(vertices, edges, source, dest):
# vertices: unique hashable objects
# edges: mapping (u, v) -> cost; u, v in vertices, cost in {-1, 1}
#vertex_costs stores the possible costs for each vertex
vertex_costs = dict((v, set()) for v in vertices)
vertex_costs[source].add(0) # source can be reached with cost 0
#vertex_costs_from stores for each the possible costs for each vertex the previous vertex
vertex_costs_from = dict()
# vertex_gotos is a convenience structure mapping a vertex to all ends of outgoing edges and their cost
vertex_gotos = dict((v, []) for v in vertices)
for (u, v), c in edges.items():
vertex_gotos[u].append((v, c))
max_c = len(vertices) ** 2 # the crucial number: maximal cost that's possible for an optimal path
todo = [(source, 0)] # which vertices to look at
while todo:
u, c0 = todo.pop(0)
for v, c1 in vertex_gotos[u]:
c = c0 + c1
if 0 <= c <= max_c and c not in vertex_costs[v]:
vertex_costs[v].add(c)
vertex_costs_from[v, c] = u
todo.append((v, c))
if not vertex_costs[dest]: # destination not reachable
return None # or raise some Exception
cost = min(vertex_costs[dest])
path = [(dest, cost)] # build in reverse order
v, c = dest, cost
while (v, c) != (source, 0):
u = vertex_costs_from[v, c]
c -= edges[u, v]
v = u
path.append((v, c))
return path[::-1] # return the reversed path
そして、いくつかのグラフの出力(エッジとその重み/パス/パスの各ポイントでのコスト。申し訳ありませんが、素敵な画像はありません):
AB+ BC+ CD+ DA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-
A B C D A X Y H I J K L M H
0 1 2 3 4 5 6 7 6 5 4 3 2 1
AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-
A B C D E F G A B C D E F G A B C D E F G A X Y H I J K L M H I J K L M H I J K L M H I J K L M H
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NH-
A X Y H
0 1 2 3
AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NO- OP- PH-
A B C D E F G A B C D E F G A B C D E F G A B C D E F G A B C D E F G A B C D E F G A X Y H I J K L M N O P H I J K L M N O P H I J K L M N O P H I J K L M N O P H I J K L M N O P H
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
その出力を生成するコードは次のとおりです。
def find_path(edges, source, path):
from itertools import chain
print(edges)
edges = dict(((u, v), 1 if c == "+" else -1) for u, v, c in edges.split())
vertices = set(chain(*edges))
path = f(vertices, edges, source, dest)
path_v, path_c = zip(*path)
print(" ".join("%2s" % v for v in path_v))
print(" ".join("%2d" % c for c in path_c))
source, dest = "AH"
edges = "AB+ BC+ CD+ DA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-"
# uv+ means edge from u to v exists and has cost 1, uv- = cost -1
find_path(edges, source, path)
edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-"
find_path(edges, source, path)
edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NH-"
find_path(edges, source, path)
edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NO- OP- PH-"
find_path(edges, source, path)