---
title: "AtCoder Beginner Contest 286 E(最短路)"
tags: 題解, 演算法, 圖論
---
https://atcoder.jp/contests/abc286/tasks/abc286_e
#### 題意
給一個 $n$ x $n$ 的矩陣代表兩個節點是否相連,再給一個 $A[i]$ 代表第 $i$ 個節點上的分數。接下來有 $Q$ 筆詢問,每筆詢問為 $u$、$v$ 兩節點之間「最短節點數的路徑之節點數」以及「所有長度為此節點數的路徑中,最大分數的路徑之分數」。
#### 思路
一般來說,使用 Floyd-Warshall 求全源最短路時,求的都是距離最短的路徑;這題特別的地方在於:除了最小化節點數以外,也要同時最大化路徑上的分數。
一個常見的做法是把每條邊的權重從 $w$ 轉化成 $C-w$,$C$ 是一個很大的數字。由於權重主要被 $C$ 主宰,找出的最短路必定會是節點數最少的;以同樣節點數的路徑而言,路徑上權重和 $\sum{w}$ 較大,則整條路徑的 $\sum{(C-w)}$會是最小,使得它會被選為最短路。如此,藉由增加一個大數字主宰邊的權重,即可同時最小化節點數以及最大化路徑分數和。
計算路徑的節點數時,假設節點數為 $n$,路徑分數和就會是 $nC - \sum{w}$,將其除以 $C$ 並取 ceil 即可得到 $n$。路徑真實的分數和則為 $nC - (nC - \sum{w})$。
#### Code
```python=1
def floyd(n, G):
dist = [[INF for _ in range(n)] for _ in range(n)]
prev = [[-1 for _ in range(n)] for _ in range(n)]
for (u, v, w) in G:
dist[u][v] = w
prev[u][v] = u
for v in range(n):
dist[v][v] = 0
prev[v][v] = v
for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][k] != INF and dist[k][j] != INF and \
dist[i][j] > dist[i][k] + dist[k][j]:
dist[i][j] = dist[i][k] + dist[k][j]
prev[i][j] = prev[k][j]
return dist, prev
def solve(_tc):
n = int(input())
A = list(map(int, input().split()))
offset = 10 ** 13
graph = []
for i in range(n):
s = input()
for (j, can) in enumerate(s):
if can == "Y":
graph.append((i, j, offset - A[j]))
D, _ = floyd(n, graph)
q = int(input())
for _ in range(q):
u, v = map(int, input().split())
u -= 1; v -= 1
ans = D[u][v]
if ans == INF:
print("Impossible")
else:
num = ans // offset + int(ans % offset != 0)
value = offset * num - ans + A[u]
print(num, value)
```