#author("2026-07-12T02:46:36+09:00;2023-02-23T23:33:35+09:00","default:vip","vip")
#author("2026-07-12T02:51:38+09:00;2023-02-23T23:33:35+09:00","default:vip","vip")
[[問題文>練習問題#critical-path]]

空入力でテストを出力。トポロジカルソートは実は自力実装しなくてもgraphlibにある。(toposort_graphlibの部分。本実装では呼んでいない。)
 # graph は隣接リスト(辞書)で管理
 
 from collections import namedtuple, defaultdict
 
 Node = namedtuple('Node' , ["v", "cost"])
 
 s = input('input:\n').strip()
 for _ in range(1):
     if s == '':
         # test data
         V, E = 5, 5
         graph = {
             'A':[Node('D',1)],
             'B':[Node('C',1)],
             'C':[Node('E',1)],
             'D':[Node('B',100), Node('C',10)],
             'E':[]}
         break
 
     V, E = map(int, s.split())
     graph = defaultdict(list)
     for _ in range(E):
       src, dst, cost = input().upper().split()
       graph[src].append(Node(dst, int(cost)))
       graph[dst]  # default [] will be made when no entry
 
 vertexes = sorted(graph.keys())
 START = 'A'
 END = vertexes[-1]
 
     
 def toposort(graph):  # 深さ優先探索(DFS)を使うタイプ
     res = []
     UNVISITED, PRE_DONE, POST_DONE = 0, 1, 2
     UNVISITED, POST, VISITED = 0, 1, 2
     visited = dict((v, UNVISITED) for v in vertexes)
     is_start_found = False
 
     stack = []
     for v in vertexes:
         if is_start_found: break
         stack.append(v)
         while stack:
             u = stack.pop()
             if visited[u] == UNVISITED:  # 行きがけ
                 visited[u] = PRE_DONE
                 visited[u] = POST
                 stack.append(u)
                 for node in graph[u]:
                     if visited[node.v]: continue
                     stack.append(node.v)
             elif visited[u] == PRE_DONE:  # 帰りがけ
                 visited[u] = POST_DONE
             elif visited[u] == POST:  # 帰りがけ
                 visited[u] = VISITED
                 res.append(u)
                 if u == START:
                     is_start_found = True
                     break
     res.reverse()
     return res
 
 def toposort_graphlib(graph):
     import graphlib
     
     # graphlib 用の隣接辞書は先行ノード管理型なので矢の向きが逆
     g = dict((v,[]) for v in graph.keys())
     for src_v, src_list in graph.items():
         for dst in src_list:
             g[dst.v].append(src_v)
     ts = graphlib.TopologicalSorter(g)
     return tuple(ts.static_order())
 
 topo_order = toposort(graph)
 
 # DP
 dists = dict((v, -1) for v in vertexes)
 dists[START] = 0
 prevs = dict((v, -1) for v in vertexes)
 for v in topo_order:
     if dists[v] == -1: continue  # STARTと繋がっていない
     for node in graph[v]:
         if dists[node.v] < dists[v] + node.cost:
             dists[node.v] = dists[v] + node.cost
             prevs[node.v] = v
 
 def get_path(t):
     path = []
     while t != -1:
         path.append(t)
         t = prevs[t]
     path.reverse()
     return path
 
 print()
 c_path = get_path(END)
 print(' -> '.join(c_path))
 print(dists[END])
 
 # 簡易チャート
 print()
 topo_s = [v for v in topo_order if dists[v] != -1]
 c_edges = [c_path[i: i+2] for i in range(len(c_path)-1)]
 print(''.join(f"{v:<7}" for v in topo_s))
 for v in topo_s:
     src_i = topo_s.index(v)
     for node in graph[v]:
         dst_i = topo_s.index(node.v)
         is_critical = [v, node.v] in c_edges
         line_char = "*" if is_critical else "-"
         line = f"{node.cost:{line_char}^{(dst_i-src_i)*7}}"
         print(' '*7*src_i + line + '>')

 A      D      B      C      E
 ***1***>
        **100**>
        ------10------>
               ***1***>
                      ***1***>

----
正しくない実装。[[入力によっては正答を返さない>#test-cases]]し、そもそも最長経路問題にダイクストラ法は上手くいかない。
 def dijk(s):
   global d, prev
   d = [0] * V
   used = [False] * V
   prev = [-1] * V
   d[s] = 0
   while True:
     v = -1
     for u in range(V):
       if v!=-1 and cost[v][u] == INF: continue
       if not used[u] and (v==-1 or d[u] > d[v]): v = u
     if v == -1: break
     used[v] = True
     for u in range(V):
       if cost[v][u] == INF: continue
       if d[u] < d[v] + cost[v][u]:
         d[u] = d[v] + cost[v][u]
         prev[u] = v
 
 def get_path(t):
   path = []
   while t != -1:
     path.append(chr(ord('A')+t))
     t = prev[t]
   return ' -> '.join(path[::-1])
 
 V, E = map(int, input('input:\n').split())
 INF = 987654321
 cost = [ [ INF for _ in range(V+1) ] for _ in range(V+1) ]
 for _ in range(E):
   a, b, c = input().split()
   a = ord(a) - ord('A')
   b = ord(b) - ord('A') 
   cost[a][b] = int(c)
 dijk(0)
 print(get_path(V-1))
 print(d[V-1])

&aname(test-cases){撃墜例};
- 例1
 3 2
 A C 1
 B C 2
#clear
 # 正答は A -> C で 1。
 B -> C  # Aが出発点にならない
 2
- 例2
 5 5
 A D 1
 D B 100
 D C 10
 B C 1
 C E 1
#clear
 # 正答は A -> D -> B -> C -> E で 103。
 A -> D -> C -> E
 2

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS