ABC164 E - Two Currencies
問題原文
問題要旨
金貨めっちゃたくさんと銀貨 枚を持って、 頂点 辺の無向連結グラフを頂点0から旅することを考える。
各辺には通行料が設定されており、銀貨 枚を払わないと通れず、移動に 分かかる。
また、各頂点には両替所が設定されており、金貨1枚を銀貨 枚に、 分で両替することができる。
頂点0から各頂点にたどり着くまでの最短時間を求めよ。
- 二重辺、自己ループなし
解法
の制約が小さいので、銀貨は最大でも2500枚ぐらいあれば十分なことがわかる。これで のデカ制約を無視できる。
状態の同一視がカギで、
頂点 にいて銀貨を 枚持っているような状態になるための最短時間
として拡張ダイクストラで解けばいい。
実装
頂点 に留まって両替だけする場合、を別でわけて書いたら見通しがよくなりそう。
import heapq N, M, init_silver = map(int, input().split()) MAX_COST = 2500 init_silver = min(init_silver, MAX_COST) G = [[] for _ in range(N)] for i in range(M): u, v, silver_cost, time_cost = map(int, input().split()) u, v = u - 1, v - 1 G[u].append([v, silver_cost, time_cost]) G[v].append([u, silver_cost, time_cost]) change_rate, change_cost = [], [] for i in range(N): rate, cost = map(int, input().split()) change_rate.append(rate) change_cost.append(cost) # dp[i][silver] := 頂点iにいて銀貨をsilver枚持っているような状況を作るために必要な最小時間 dp = [[float('inf')] * (MAX_COST + 1) for _ in range(N)] dp[0][init_silver] = 0 # 優先度付きキュー: (time, node, silver) hq = [(0, 0, init_silver)] while hq: time, node, silver = heapq.heappop(hq) self_loop_silver = min(silver + change_rate[node], MAX_COST) self_loop_cost = time + change_cost[node] if self_loop_cost < dp[node][self_loop_silver]: dp[node][self_loop_silver] = self_loop_cost heapq.heappush(hq, (time + change_cost[node], node, self_loop_silver)) for to, silver_cost, time_cost in G[node]: remain_silver = min(silver - silver_cost, MAX_COST) if remain_silver < 0: continue dp_next_value = time + time_cost if dp[to][remain_silver] <= dp_next_value: continue dp[to][remain_silver] = dp_next_value heapq.heappush(hq, (dp_next_value, to, remain_silver)) print(*[min(d) for d in dp[1:]], sep="\n")
銀貨コストを にして、 時間コストを 、移動先を自身とするような自己ループを各頂点に追加してもできる。
こっちのがダイクストラ部分の見通しがよい、けどちょっとだけ思考レベルが高そう。(しらんけど)
import heapq N, M, init_silver = map(int, input().split()) MAX_COST = 2500 init_silver = min(init_silver, MAX_COST) G = [[] for _ in range(N)] for i in range(M): u, v, silver_cost, time_cost = map(int, input().split()) u, v = u - 1, v - 1 G[u].append([v, silver_cost, time_cost]) G[v].append([u, silver_cost, time_cost]) change_rate, change_cost = [], [] for i in range(N): rate, cost = map(int, input().split()) G[i].append([i, -rate, cost]) # これを追加! change_rate.append(rate) change_cost.append(cost) # dp[i][silver] := 頂点iにいて銀貨をsilver枚持っているような状況を作るために必要な最小時間 dp = [[float('inf')] * (MAX_COST + 1) for _ in range(N)] dp[0][init_silver] = 0 # 優先度付きキュー: (time, node, silver) hq = [(0, 0, init_silver)] while hq: time, node, silver = heapq.heappop(hq) for to, silver_cost, time_cost in G[node]: remain_silver = min(silver - silver_cost, MAX_COST) if remain_silver < 0: continue dp_next_value = time + time_cost if dp[to][remain_silver] <= dp_next_value: continue dp[to][remain_silver] = dp_next_value heapq.heappush(hq, (dp_next_value, to, remain_silver)) print(*[min(d) for d in dp[1:]], sep="\n")
感想
本番なんか無限にバグらせたけど、今見たらサクッと解けてやるせない気持ちになった。
落ち着こう、ね。