问题描述
B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
给出 B 地区的村庄数 N,村庄编号从 0 到 N - 1,和所有 M 条公路的长度,公路是双向的。并给出第 i 个村庄重建完成的时间 ti ,你可以认为是同时开始重建并在第 ti 天重建完成,且在当天即可通车。若 ti 为 0 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 Q 个询问 ( x , y , t ) ,对于每个询问你要回答在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少。如果无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x 或村庄 y 在第 t 天仍未重建完成,则需要返回 -1 。
输入格式
第一行包含两个正整数 N , M , 表示了村庄的数目与公路的数量。
第二行包含 N 个非负整数 t0,t1,… ,t(N-1),表示了每个村庄重建完成的时间,数据保证了 t0 ≤ t1 ≤ … ≤ t(N-1) 。
接下来 M 行,每行 3 个非负整数 i , j , w,w 为不超过 10000 的正整数,表示了有一条连接村庄 i 与村庄 j 的道路,长度为w,保证 i ≠ j,且对于任意一对村庄只会存在一 条道路。
接下来一行也就是 M + 3 行包含一个正整数 Q,表示Q个询问。
接下来 Q 行,每行 3 个非负整数 x , y , t,询问在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少,数据保证了 t 是不下降的。
输出格式
共 Q 行,对每一个询问 ( x , y , t ) 输出对应的答案,即在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少。如果在第 t 天无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x 或村庄 y 在第 t 天仍未修复完成,则输出−1。
输入样例
4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4
输出样例
-1
-1
5
4
说明/提示
对于 30% 的数据,有 N ≤ 50 ;
对于 30% 的数据,有 ti = 0,其中有 20% 的数据有 ti = 0 且 N > 50;
对于 50% 的数据,有 Q ≤ 100 ;
对于 100% 的数据,有 N ≤ 200,M ≤ N * (N-1)/ 2 ,Q ≤ 50000,所有输入数据涉及整数均不超过 100000。
算法思路
题目本质上就是求多源点最短路问题,由于数据较小,所以可以用Floyd算法。
又因为每个村庄的重建时间是升序的,并且每个询问中的 t 也是不下降的,所以可以用在线解法。
每输入一次询问,就将重建时间小于 t 的村庄加入到规划中,更新dist矩阵。保存此次询问的答案。
最后将每次询问保存的答案ans数组输出。
实现代码C++
// 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// 洛谷P1119 - 灾后重建
#include <iostream>
using namespace std;
const int MAX_N = 200;
const int MAX_M = MAX_N * (MAX_N - 1) / 2;
const int MAX_DIST = 100001;
const int MAX_Q = 50000;
void BuildMinDist(int dist[MAX_N][MAX_N], int N, int k)
{
int i, j;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
if (dist[i][k] + dist[k][j] < dist[i][j])
{
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
int getAns(int dist[MAX_N][MAX_N], int RebuildDates[], int x, int y, int t)
{
int ans = -1;
if (RebuildDates[x] > t || RebuildDates[y] > t || dist[x][y] == MAX_DIST)
{
ans = -1;
}
else
{
ans = dist[x][y];
}
return ans;
}
// 输出dist矩阵,调试使用。
void printdist(int dist[][MAX_N],int N)
{
cout << "---start-------------" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cout << dist[i][j] << "\t\t";
}
cout << endl;
}
cout << "---end-------------" << endl;
}
int main()
{
int N; // 村庄数
int M; // 公路数
int i, j, w; // 连接村庄i和村庄j的道路长度为w
int Q; // 询问数
int x, y, t; // 在第t天,从村庄x到村庄y
int dist[MAX_N][MAX_N]; // 村庄之间的距离矩阵
int RebuildDates[MAX_N]; // 村庄的重建时间
int ans[MAX_Q];
// 输入村庄数、公路数
cin >> N >> M;
// 初始化dist矩阵
for (int a = 0; a < N; a++)
{
for (int b = 0; b < N; b++)
{
if (a == b)
{
dist[a][b] = 0;
}
else
{
dist[a][b] = MAX_DIST;
}
}
}
// 输入每个村庄的重建时间
for (int a = 0; a < N; a++)
{
cin >> RebuildDates[a];
}
// 输入每条公路的长度
for (int a = 0; a < M; a++)
{
cin >> i >> j >> w;
dist[i][j] = w;
dist[j][i] = w;
}
// 输入询问数
cin >> Q;
// 输入Q组询问并保存每组询问的答案。k代表待重建的村庄编号。
int k = 0;
for (int a = 0; a < Q; a++)
{
cin >> x >> y >> t;
while (RebuildDates[k] <= t && k < N)
{
// 在线构建最短距离矩阵
BuildMinDist(dist, N, k);
k++;
}
ans[a] = getAns(dist, RebuildDates, x, y, t);
}
// 输出答案
for (int a = 0; a < Q; a++)
{
cout << ans[a] << endl;
}
return 0;
}