Submission #12916854


Source Code Expand

# 以下を参考にした:
# https://atcoder.jp/contests/abc164/submissions/12550661

# ツール

class PriorityQueue
	def initialize
		@heap = []
	end

	def push value
    @heap.push value
		update_heap_to_up
	end

	def shift
		min, last = @heap[0], @heap.pop
		unless @heap.empty?
			@heap[0] = last
			update_heap_to_down
		end
		min
	end

  private

	def update_heap_to_up
    index = @heap.size - 1
		up_value = @heap[index]
		while 0 <= (parent_index = (index-1)/2)
      parent_value = @heap[parent_index]
			break if parent_value <= up_value
      @heap[index] = parent_value
			index = parent_index
		end
		@heap[index] = up_value
	end

	def update_heap_to_down
    index = 0
    down_value = @heap[0]
    size = @heap.size
		while (child_index = 2*index+1) < size
      # 右の子がいて、右の子が小さければ、右の子を使う
			child_index += 1 if child_index+1 < size and @heap[child_index+1] < @heap[child_index]
      child_value = @heap[child_index]
			break if down_value <= child_value
			@heap[index] = child_value
      index = child_index
		end
		@heap[index] = down_value
	end
end

# 都市間の移動コストは最大50
# 都市は最大50なので、必要な最大コストは
# 50 * (50-1) = 2450
MAX_COST = 2450

def encode(time, money, city)
  (time << 18) | ([money, MAX_COST].min << 6) | city
end

def decode(value)
  # 4095 = 0b0000_1111_1111_1111 (12桁のマスク)
  #   63 = 0b0000_0000_0011_1111 ( 6桁のマスク)
  [(value >> 18), ((value >> 6) & 4095), (value & 63)]
end

# 入力

n, m, init_money = gets.split.map(&:to_i)

adj = n.times.map{Array.new}
m.times do
	from, to, money, time = gets.split.map(&:to_i)
  adj[from-1].push [to-1, money, time]
  adj[to-1].push [from-1, money, time]
end

exchange = n.times.map do
	money, time = gets.split.map(&:to_i)
	lambda do |current, target|
    need_count = (target - current + money - 1)/money # = ((target - current) / money.to_f).ceil
		[money * need_count, time * need_count]
  end
end

# メイン

reached = 0
min_time = Array.new(n, nil)  # 各都市に最初についた時間

max_money = Array.new(n, -1)  # 各都市で所持してた金額の最大値
need_money = Array.new(n, 0)  # 各都市で最低限必要となる金額
last_state = Array.new(n, nil)  # 各都市で最後に追加した状態

queue = PriorityQueue.new
queue.push encode(0, init_money, 0)

while state = queue.shift
	current_time, current_money, current_city = decode(state)

  # ループの後の方が時間が遅い(時間が早い順に処理してるため)
  # なので、かつて訪問していて所持金が以前の値以下の場合、
  # 結果が改善されることはないので探索をカットする
	next if current_money <= max_money[current_city]
	max_money[current_city] = current_money

	unless min_time[current_city] # 最初の訪問
		reached += 1
    min_time[current_city] = current_time
		break if reached == n # 完了
	end

	min_need_money = MAX_COST
	adj[current_city].each do |to_city, move_cost, move_time|
    # 移動後の所持金が以前の値以下だと意味がないので、
    # 以前の値より大きくなる場合だけノードを追加する
		if current_money - move_cost > max_money[to_city]
      next_money = current_money - move_cost
      next_time = current_time + move_time

      # 移動後に最低限必要となる金額未満でノードを登録すると両替が必要となるので、
      # 移動後に最低限必要な金額まであらかじめ両替しておく
      if next_money < need_money[to_city]
        exchange_money, exchange_time = exchange[to_city].call(next_money, need_money[to_city])
        next_money += exchange_money
        next_time += exchange_time
      end

      next_state = encode(next_time, next_money, to_city)
			if last_state[to_city] != next_state
				last_state[to_city] = next_state
				queue.push next_state
			end
		end

    # 現在の金額以下、もしくは(次の都市での所持金+移動コスト)以下でこの都市に来ても意味がない
    # すべての隣接都市に対しての下限を得る
    current_need_money = [current_money, max_money[to_city] + move_cost].max + 1
		min_need_money = [current_need_money, min_need_money].min
  end
	need_money[current_city] = min_need_money

  # 最低限必要な金額まで両替する
  exchange_money, exchange_time = exchange[current_city].call(current_money, min_need_money)
  next_state = encode(current_time + exchange_time, current_money + exchange_money, current_city)
	if last_state[current_city] != next_state
		last_state[current_city] = next_state
		queue.push next_state
	end
end

# 出力

min_time.shift
puts min_time.join("\n")

Submission Info

Submission Time
Task E - Two Currencies
User yappy0625
Language Ruby (2.7.1)
Score 500
Code Size 4894 Byte
Status AC
Exec Time 146 ms
Memory 14492 KiB

Judge Result

Set Name Sample All
Score / Max Score 0 / 0 500 / 500
Status
AC × 5
AC × 27
Set Name Test Cases
Sample sample_01.txt, sample_02.txt, sample_03.txt, sample_04.txt, sample_05.txt
All line_1.txt, line_2.txt, random_01.txt, random_02.txt, random_03.txt, random_04.txt, random_05.txt, random_06.txt, random_07.txt, random_08.txt, random_09.txt, random_10.txt, random_11.txt, random_12.txt, random_13.txt, random_14.txt, random_15.txt, random_16.txt, random_17.txt, random_18.txt, random_19.txt, random_20.txt, sample_01.txt, sample_02.txt, sample_03.txt, sample_04.txt, sample_05.txt
Case Name Status Exec Time Memory
line_1.txt AC 52 ms 14160 KiB
line_2.txt AC 146 ms 14220 KiB
random_01.txt AC 57 ms 14152 KiB
random_02.txt AC 62 ms 14216 KiB
random_03.txt AC 55 ms 14108 KiB
random_04.txt AC 60 ms 14268 KiB
random_05.txt AC 66 ms 14088 KiB
random_06.txt AC 58 ms 14132 KiB
random_07.txt AC 55 ms 14128 KiB
random_08.txt AC 58 ms 14268 KiB
random_09.txt AC 55 ms 14260 KiB
random_10.txt AC 67 ms 14296 KiB
random_11.txt AC 55 ms 14312 KiB
random_12.txt AC 56 ms 14280 KiB
random_13.txt AC 61 ms 14324 KiB
random_14.txt AC 56 ms 14256 KiB
random_15.txt AC 56 ms 14312 KiB
random_16.txt AC 60 ms 14308 KiB
random_17.txt AC 66 ms 14188 KiB
random_18.txt AC 56 ms 14320 KiB
random_19.txt AC 54 ms 14492 KiB
random_20.txt AC 57 ms 14268 KiB
sample_01.txt AC 52 ms 14144 KiB
sample_02.txt AC 55 ms 14268 KiB
sample_03.txt AC 53 ms 14312 KiB
sample_04.txt AC 52 ms 13988 KiB
sample_05.txt AC 54 ms 14144 KiB