---
# System prepended metadata

title: AtCoder Beginner Contest 286 E(最短路)
tags: [' 圖論', 題解, ' 演算法']

---

---
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)
```