LuoguP1119 灾后重建
Description
有N个村庄,村庄编号从0到N−1,M条公路,公路是双向的。并给出第i个村庄重建完成的时间t_i,你可以认为是同时开始重建并在第t_i 天重建完成,并且在当天即可通车。若t_i为0则说明地
震未对此地区造成损坏,一开始就可以通车。之后有Q个询问(x, y, t),对于每个询问你要回答在第t天,从村庄x到村庄y的最短路径长度为多少。如果无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未重建完成 ,则需要返回-1。
Analysis
这道题考察floyd的本质,若用朴素的floyd,循环Q次,每次都要跑O(N3)的Floyd,则复杂度为O(N3Q)。这里其实不需要枚举最外层循环k,如果在某个时间t,找到一个_T[k] <= t,则将其作为中间点更新最短距离。这样每次询问都找到时间小于t的k,k不断自加,最终时间复杂度O(N^3 + Q)。(我也不知道为什么T[]不排序也可以AC)
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define N 210
using namespace std;
int G[N][N],T[N],_T[N],n,m,q,x,y,t,k;
void floyd(){
// P.S. 我就是忘了 k < n只得了40分
//因为T初始为0
//当然也可以初始化T[n] = inf就不用判断了
while(T[k] <= t && k < n){
for(int i = 0;i < n;++i){
for(int j = 0;j < n;++j){
G[i][j] = min(G[i][j],G[i][k] + G[k][j]);
}
}
++k;
}
}
void solve(){
floyd();
if(T[x] > t || T[y] > t || G[x][y] == 0x3f3f3f3f) printf("-1\n");
else printf("%d\n",G[x][y]);
}
int main()
{
scanf("%d%d",&n,&m);
memset(G,0x3f,sizeof(G));
for(int i = 0;i < n;++i){
G[i][i] = 0;
scanf("%d",&T[i]);
}
for(int i = 1;i <= m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u][v] = G[v][u] = w;
}
scanf("%d",&q);
for(int i = 1;i <= q;++i){
scanf("%d%d%d",&x,&y,&t);
solve();
}
return 0;
}
岂能尽如人意,但求无愧我心