题目描述
难度分:1128
输入n(2≤n≤300)和长为n的数组a(1≤a[i]≤109),表示一个n个点的图,每个点的点权为a[i]。
然后输入一个n×n的YN
矩阵g,其中g[i][j]=Y
表示有一条从i到j的有向边,边权为1;g[i][j]=N
表示没有i到j的有向边。保证g[i][i]=N
。
然后输入q(1≤q≤n×(n−1))和q个询问,每个询问输入两个数x和y,保证x≠y,范围在[1,n]。
对于每个询问,输出两个数:
- 从x到y的最短路长度d。
- 在所有从x到y的长为d的路径中,路径点权之和的最大值。
如果无法从x到y,输出Impossible
。
输入样例1
5
30 50 70 20 60
NYYNN
NNYNN
NNNYY
YNNNN
YNNNN
3
1 3
3 1
4 5
输出样例1
1 100
2 160
3 180
输入样例2
2
100 100
NN
NN
1
1 2
输出样例2
Impossible
算法
Floyd算法
比较容易想到用Floyd算法来求多源汇最短路,因为它的本质是动态规划,扩展性还是蛮强的。假设dist[x][y]是x到y的最短路,w[x][y]是x到y在保持最短路的情况下的最大点权和。
- 如果dist[i][j]>dist[i][k]+dist[k][j],那么进行状态转移dist[i][j]=dist[i][k]+dist[k][j],w[i][j]=w[i][k]+w[k][j]−a[k]。减去a[k]是因为这个状态转移下,中间点k的权值被加了两次,需要减去一次。
- 如果dist[i][j]=dist[i][k]+dist[k][j],那么进行状态转移w[i][j]=max(w[i][j],w[i][k]+w[k][j]−a[k])。
之后q个查询,每个O(1)就可以从w矩阵中查到答案。
复杂度分析
时间复杂度
Floyd算法的时间复杂度为O(n3)。每条询问的处理是O(1)的,处理所有询问的时间复杂度为O(q)。因此,整个算法的时间复杂度为O(n3+q)。
空间复杂度
除了输入的点权数组a和图的邻接矩阵g,空间消耗的瓶颈在于w和dist两个矩阵,额外空间复杂度为O(n2)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 310, INF = 0x3f3f3f3f;
char s[N][N];
int n, a[N], dist[N][N];
LL w[N][N];
int main() {
scanf("%d", &n);
memset(w, 0, sizeof w);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
for(int j = 1; j <= n; j++) {
if(s[i][j] == 'Y') {
dist[i][j] = 1;
w[i][j] = max(w[i][j], 1LL * a[i] + a[j]);
}else {
dist[i][j] = INF;
}
}
}
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
w[i][j] = w[i][k] + w[k][j] - a[k];
}else if(dist[i][k] + dist[k][j] == dist[i][j]) {
w[i][j] = max(w[i][j], w[i][k] + w[k][j] - a[k]);
}
}
}
}
int q;
scanf("%d", &q);
while(q--) {
int x, y;
scanf("%d%d", &x, &y);
if(dist[x][y] < INF) {
printf("%d %lld\n", dist[x][y], w[x][y]);
}else {
puts("Impossible");
}
}
return 0;
}