灾后重建(最短路问题 Floyd)
题目描述
给出B地区的村庄数N,村庄编号从0到N - 1,和所有M条公路的长度,公路是双向的。并给出第个村
庄重建完成的时间ti,你可以认为是同时开始重建并在第t;天重建完成,并且在当天即可通车。若t;为0则
说明地震未对此地区造成损坏,-开始就可以通车。之后有Q个询问(x, y,t),对于每个询问你要回答在第
t天,从村庄x到村庄y的最短路径长度为多少。如果无法找到从x村庄到y村庄的路径,经过若干个已重建
完成的村庄,或者村庄x或村庄y在第t天仍未重建完成,则需要返回-1。
输入格式
第一行包含两个正整数N, M,示了村庄的数目与公路的数量。
第二行包含N个非负整数to,t....,tN-1.示了每个村庄重建完成的时间,数据保证了to < t1≤
..≤tN-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。
最开始本人用的是dijstra,然后成功卡在了80分,我想我主要的问题是对已求得数据的复用率不高导致超时……而且本题作者人很好的一点是,建村所需时间和查询天数都是依次有序不减的(当时的我忽略了这个问题导致百思不得其解,并且有
这个前提的话选择Floyd而不是dijstra的理由就说得通了,dijstra对于上一轮求得的数据,若起点改变,则必须重新计算,数据的有序性在这并没有占到什么优势,也可能是我太弱暂时没想到解决办法。而Floyd这个算法的主要思路,就是通过其他的点进行中转来求的两点之间的最短路。因为我们知道,两点之间有多条路,如果换一条路可以缩短距离的话,就更新最短距离。而它最本质的思想,就是用其他的点进行中转,从而达到求出最短路的目的。对于它是如何占到便宜的,接下来我会在代码中直接说明)
#include<iostream> #include<vector> #include<cstdio> using namespace std; const int MAX = 9999999; struct find{ int bgin; int end; int value; }; int a[300][300]; inline void change(int i1,int v_n){ // 将点 i1 纳入缩短路径的选择中 for(int j = 0;j < v_n;j++) //由于是建村消耗的时间是递增的,因此若bgin和end已经建好可通车,
// 用于松弛的i1一定在可通车的那群点中 for(int k = 0;k < v_n;k++) // if(a[j][k] > a[j][i1] + a[i1][k]) a[j][k] = a[j][i1] + a[i1][k]; } int main(){ int r_n; int v_n; cin >> v_n >> r_n; //建村时间 int time[v_n]; for(int i = 0;i < v_n;i++) cin >> time[i];
//矩阵存路 for(int i = 0;i < v_n;i++) for(int j = 0;j < v_n;j++){ if(i == j) a[i][j] = 0; else a[i][j] = MAX; } for(int i = 0;i < r_n;i++){ int bg,ed,v; cin >> bg >> ed >> v; a[bg][ed] = v; a[ed][bg] = v; } int n_find; cin >> n_find; struct find Find[n_find]; vector<int> result; for(int i = 0;i < n_find;i++){ cin >> Find[i].bgin >> Find[i].end >> Find[i].value ; }
//n_find轮查询 int i3 = 0; for(int i9 = 0;i9 < n_find;i9++){ int bgin = Find[i9].bgin; int end = Find[i9].end ; if(time[bgin] > Find[i9].value || time[end] > Find[i9].value){ result.push_back(-1); continue; } while(i3 < v_n &&time[i3] <= Find[i9].value ){ change(i3,v_n); i3++; //由于Find是递增的,所以上一轮已经松弛得到的结果可以为这轮结果做铺垫,
//本轮只需要从还未纳入松弛的点考虑,对数据的复用缩短了查询时间 } if(a[bgin][end] == MAX) result.push_back(-1); else result.push_back(a[bgin][end]); } for(int i = 0;i < result.size() ;i++) cout << result[i] << endl; return 0; }