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
");
	else printf("%d
",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;
}
岂能尽如人意,但求无愧我心
原文地址:https://www.cnblogs.com/Zforw/p/10541853.html