トップページ -> AOJの解答例 -> GRL_1の解答例

GRL_1の解答例(Python)

単一始点最短経路,単一始点最短経路(負の重みを含む場合),全点対間最短経路に関する問題です. GRLはグラフ理論に関する問題ですのでこちらの参考書を参考にしました. 本ページでは解答のみの紹介に留めアルゴリズムの解説は行いません. 対応するトピックがどの本の何ページに載っているかや参考になるサイトは適宜紹介します.

1. GRL_1_A: Single Source Shortest Path

単一始点最短経路に関する問題です. 優先度付きキューを使ってダイクストラ法(Dijkstra)を実装しました.
負の重みが含まれない場合は優先度付きキューから重みの小さい辺から取り出してコストを更新することで単一始点最短経路を解くことができます. ※ アルゴリズムイントロダクション 219P

# GRL_1_A
import heapq
V,E,r = map(int,input().split())

adjacent_list = [[] for _ in range(V)]
for _ in range(E):
    s,t,w = map(int,input().split())
    adjacent_list[s].append((w,s,t)) # 重みで優先度を付けたいのでwを1番目にする
    
# 始点からの辺を記憶した優先度付きキューを作る
heap = [info for info in adjacent_list[r]]
heapq.heapify(heap)

min_cost = [float("inf") for _ in range(V)]
min_cost[r] = 0
while heap:
    # 重みが小さい順に取り出す
    w,s,t = heapq.heappop(heap)
    # コストが小さくなる場合はコストを更新する
    if min_cost[t] > min_cost[s] + w:
        min_cost[t] = min_cost[s] + w
        # コストが更新されたので先の経路を調べる
        for info in adjacent_list[t]:
            heapq.heappush(heap,info)
        
for m in min_cost:
    if m == float("inf"):
        print("INF")
    else:
        print(m)

2. GRL_1_B: Single Source Shortest Path (Negative Edges)

Aと同じ単一始点最短経路ですが,今回は負の重みを含みますのでダイクストラ法が使えません. 負の重みを含む場合はベルマン-フォード法(Bellman-Ford)を使います. ※ アルゴリズムイントロダクション 223P

# GRL_1_B

from collections import deque

def BellmanFord():
    V,E,r = map(int,input().split())

    adjacent_list = [[] for _ in range(V)]
    for _ in range(E):
        s,t,w = map(int,input().split())
        adjacent_list[s].append((w,s,t)) 

    cost_list = [float("inf") for _ in range(V)]
    cost_list[r] = 0
    
    # 各辺に関してV-1回更新できるか確かめる
    for _ in range(V):
        for i in range(V):
            for edge in adjacent_list[i]:
                w,s,t = edge
                if cost_list[t] > cost_list[s] + w:
                    cost_list[t] = cost_list[s] + w
      
    # V回目の更新ができてしまったら負の閉路を含む
    for i in range(V):
        for edge in adjacent_list[i]:
            w,s,t = edge
            if cost_list[s] + w < cost_list[t]:
                # print(cost_list)
                return False

    return cost_list

cost_list = BellmanFord()
if cost_list:
    for c in cost_list:
        if c == float("inf"):
            print("INF")
        else:
            print(c)
else:
    print("NEGATIVE CYCLE")

3. GRL_1_C: All Pairs Shortest Path

全点対間最短経路です. フロイド-ワーシャル法(Floyd-Warshall)を使います. 隣接リストではなく隣接行列を使います. ※ アルゴリズムイントロダクション 246P

# GRL_1_C
def FloydWarshall():
    V,E = map(int,input().split())

    adjacent_array = [[float("inf") for _ in range(V)] for _ in range(V)]
    # 対角成分を0にする
    for i in range(V):
        adjacent_array[i][i] = 0
    
    for _ in range(E):
        s,t,w = map(int,input().split())
        adjacent_array[s][t] = w # 重みで優先度を付けたいのでwを1番目にする
        
    D = adjacent_array
    for k in range(V):
        D_next = [[0 for _ in range(V)] for _ in range(V)]
        for i in range(V):
            for j in range(V):
                # D[i][k]+D[k][j] の方が小さい場合は k を経由すればコストを小さくできる
                D_next[i][j] = min(D[i][j],D[i][k]+D[k][j])
                
        D = D_next[:]
        
    # 負の閉路の長さは高々V以下なので閉路を含まないかをV回更新して確かめる
    for k in range(V):
        D_next = [[0 for _ in range(V)] for _ in range(V)]
        for i in range(V):
            for j in range(V):
                D_next[i][j] = min(D[i][j],D[i][k]+D[k][j])
                # 更新できてしまった場合は閉路を含むのでFalseを返す
                if D[i][j] > D[i][k]+D[k][j]:
                    return False
                
        D = D_next[:]

    D = D_next[:]
        
    return D

ans = FloydWarshall()
if ans:
    for cost_list in ans:
        for i in range(len(cost_list)):
            if i != len(cost_list)-1:
                if cost_list[i] == float("inf"):
                    print("INF",end=" ")
                else:
                    print(cost_list[i],end=" ")
            else:
                if cost_list[i] == float("inf"):
                    print("INF")
                else:
                    print(cost_list[i])
else:
    print("NEGATIVE CYCLE")

【目次に戻る】 次へ進む ->