トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

ダイクストラ法

[アルゴリズム]

練習のための問題は 競技プログラミングの履歴

グラフの距離を測るという点では グラフアルゴリズム の深さ優先探索や幅優先探索の発展版ということができる気がする。

プリム法 も参照。

TODO: 重みの合計値が一定以内に収まる経路の探索ってどうやって求める?

ダイクストラ法

  詳しい解説

疑似コード

  AOJの関連する問題

  計算量 O(V^2)

オリジナルのアルゴリズムは隣接行列をしようするため計算量がV^2

ALDS1_12_B

解答

INF  = 999999999
Node = Struct.new(:u, :d, :c, :p)
 
class Graph
  attr :nodes, :mat, :u_start
  def initialize(n, matrix, u_start_with = 1)
    @nodes = Array.new(n){ Node.new }
    @nodes.each_with_index{|node,idx| node.u = idx+u_start_with}
    @u_start = u_start_with
    @mat   = matrix.dup
  end
 
  def edge_exists?(row, col)
    #printf "  edge_exists?(#{row}, #{col}) => "
    row -= @u_start
    col -= @u_start
    result = if @mat[row][col] != -1 # or @mat[col][row] != -1
               true
             else
               false
             end
    #puts "#{result} => cost: #{@mat[row][col]}"
    result
  end
 
  def gen_uniq_edges()
    edges = []
    @nodes.each_with_index do |node,i|
      mat[i].map.with_index.select do |w,idx|
        w != -1
      end.each do |w,idx|
        edge = [node.u, [email protected]_start].sort
        edges << edge
      end
    end
    edges.uniq
  end
 
  def dot_before_dijkstra()
    puts "graph beforeDijkstra {"
    gen_uniq_edges.each do |e|
      puts "  \"#{e.first}\" -- \"#{e.last}\" [label=#{mat[[email protected]_start][[email protected]_start]}]"
    end
    puts "}"
  end
 
  def dot_after_dijkstra(start = 1)
    # start dijkstra
    dijkstra(start)
 
    # then, generate dot
    puts "graph afterDijkstra {"
    gen_uniq_edges.each do |e|
      no = mat[e.first-1.to_i].detect {|m| m != -1 and (m == e.last-1 or m == e.first-1) }
      if no.nil?
        puts "  \"#{e.first}\" -- \"#{e.last}\" [label=\"w=#{mat[e.first-1][e.last-1]}\"]"
      else
        puts "  \"#{e.first}\" -- \"#{e.last}\" [label=\"w=#{mat[e.first-1][e.last-1]} (#{no})\", penwidth=3]"
      end
    end
    puts "}"
  end
 
  def dijkstra(start = 1)
    @nodes = @nodes.each do |node|
      node.d = INF
      node.p = nil
      node.c = :white
    end
 
    start = ([email protected]_start).to_i
    @nodes[start].d = 0
    @nodes[start].p = -1
    next_u = nil
 
    while true
      mincost = INF
      @nodes.each do |node|
        if node.c != :black and node.d < mincost
          #puts "node.d = #{node.d}"
          mincost = node.d
          next_u  = node.u
        end
      end
      #puts "mincost = #{mincost}, next_u = #{next_u}"
 
      break if mincost == INF
 
      @nodes[next_u].c = :black
 
      @nodes = @nodes.map.with_index do |node, v|
        if node.c != :black and edge_exists?(next_u, v) and @nodes[next_u].d + mat[next_u][v] < node.d
          node.d = @nodes[next_u].d + mat[next_u][v]
          node.p = next_u
          node.c = :gray
          node
        else
          node
        end
      end
    end
  end
end
 
lines = $stdin.read
array = lines.split("\n")
 
N   = array[0].to_i
mat = Array.new(N).map{Array.new(N, -1)}
 
for i in 1..N
  u    = array[i].split(" ").first.to_i
  k    = array[i].split(" ")[1].to_i
  cols = array[i].split(" ").drop(2)
  cols = cols.each_slice(2).to_a
 
  #debug = "u => #{u}, k => #{k}"
  cols.each do |col|
    v,w = col.first.to_i,col.last.to_i
    #debug += "| v,w = #{v},#{w} "
    mat[u][v] = w
    #mat[v][u] = w
  end
  #puts debug
end
 
# 0-indexed
graph = Graph.new(N, mat, 0)
 
# start-with 0
# graph.dot_before_dijkstra()
graph.dijkstra(0)
 
sum = 0
 
graph.nodes.each{|n| puts "#{n.u} #{n.d}"}

  計算量 O((E+V) log V)

ALDS1_12_C

解答


graph-prim.png graph-prim2.png
お名前: コメント: