【Floyd】AtCoder ABC286 E.Souvenir
题目
https://atcoder.jp/contests/abc286/tasks/abc286_e
题意
共 \(n(2 \leq n \leq 300)\) 个城市,对于每个城市输入 \(A_i(1 \leq A_i \leq 10^9)\) 代表该城市的值;
随后输入一张 \(n \times n\) 的图 \(a\),若 \(a_{i,j} = Y\) 代表该图存在一条从城市 \(i\) 到城市 \(j\) 的有向边,边长为 \(1\),若 \(a_{i,j} = N\) 则表示没边。
输入一个正整数 \(q(1 \leq q \leq n \times (n-1))\),代表进行 \(q\) 次询问:每次询问,输入两个城市编号 \(u, v(1 \leq u, v \leq n)\),保证 \(u != v\),若城市 \(u\) 无法通过某个路径抵达城市 \(v\),则输出"Impossible",否则输出 \(s, t\),其中 \(s\) 代表从 \(u\) 到 \(v\) 的最短路径长度,\(t\) 代表从 \(u\) 到 \(v\) 的最短路径中城市的值之和的最大值。
题解
对于最短路,只需要使用 Floyd 算法进行维护。
对于路径上的节点值之和的最大值,先创建一张 \(n \times n\) 的图 \(w\),将 \(A_i\) 赋值给 \(w_{i,i}\),随后根据 \(a\) 中值为 \(a_{j,k} = Y\) 初始化出 \(w_{j,k} = w_{j,j} + w_{k,k}\)。
Floyd 算法的执行过程中,每次都是尝试引入一个节点作为中转节点。对于城市 \(i\) 和城市 \(j\),若引入城市 \(k\) 作为中转节点,城市的值的大小便是 \(w_{i,k} + w_{k,j} - w_{k,k}\),需要减去一份中转节点的值 \(w_{k,k}\) 的原因是路径 \({i,k}\) 和 \({k,j}\) 已经各自包含了一次节点 \(k\) 的值。
参考代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr ll INF = 1e18;
int n, q, x, y;
char c;
ll d[300][300], p[300][300];
void floyd() {
for (int k = 0; k < n; ++ k) {
for (int i = 0; i < n; ++ i) {
if (i == k || d[i][k] == INF) continue;
for (int j = 0; j < n; ++ j) {
if (i == j || k == j || d[k][j] == INF) continue;
ll sum = d[i][k] + d[k][j];
if (sum < d[i][j]) {
d[i][j] = sum;
p[i][j] = p[i][k] + p[k][j] - p[k][k];
} else if (sum == d[i][j]) {
p[i][j] = max(p[i][j], p[i][k] + p[k][j] - p[k][k]);
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n;
for (int i = 0; i < n; ++ i) cin >> p[i][i];
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
cin >> c;
if (c == 'Y') {
d[i][j] = 1;
p[i][j] = p[i][i] + p[j][j];
} else d[i][j] = INF;
}
}
floyd();
cin >> q;
while (q --) {
cin >> x >> y;
-- x, -- y;
if (d[x][y] == INF) cout << "Impossible\n";
else cout << d[x][y] << ' ' << p[x][y] << '\n';
}
}
浙公网安备 33010602011771号