此题难点在于询问的次数足足有50k次,任何一个最短路算法去跑个50k次原图基本上都是没戏了。
在最短路的题目中,如果节点数量非常的少,那就是在提示我们使用Floyd了。
inline void floyd(){
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(i!=j)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
大部分人只知道使用floyd
却不知道floyd为何正确
根据floyd的式子我们可以得出其实就是这个递推式子
如果当k=1的时候发生了中转,但是我的最小值其实只需要在3和4的时候中转,那这个式子是不是错了呢?
若在1处发生中转,中转之后只有两种情况:1.比3更短 2.比3更长 不考虑相等的情况
1.如果更短,和原答案不符
2.如果更长,肯定会被更新成从3中转
证毕
故不会出现上述情况。
扩展至三维数组用定义也可简单证明
设总结点n
定义f[k][i][j]为i->j 只能通过 前k个节点的最短路径
根据定义可以知道k=n时为i->j在整个图中的最短路径
根据定义可以推出以下式子
1.经过k节点,最短路不能被更新.f[k][i][j]=f[k-1][i][j]
2.经过k节点,最短路能被更新.f[k][i][j]=f[k-1][i][k]+f[k-1][k][j]
综合1.2.即f[k][i][j]=min(f[k-1][i][j],f[k-1][i][k]+f[k-1][k][j])
由此可见k可以直接拿到外面去循环
再看题目所给的时间 t 都十分巧妙的保持了升序。
所以只要根据原三维dp思想进行一遍floyd就可以做出来了,因为城市可中转范围是从1~n有序的,而不是乱序的。
假设第一次询问第三天,到第三天为止已经有1~4城市能中转了,那么外部循环只要k=1~k=4范围都跑一次就可以得到答案。
因为时间是降不下来的,所以k是逐渐扩大的,那么微调一下k就可以解决这道题。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int MAX=INT_MAX>>1;
int n,m,t[200],Q,u,v,w,x,y,qt,now;
int e[200][200];
inline void inite(){
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
e[i][j]=MAX;
for(int i=0;i<n;++i)
e[i][i]=0;
}
inline void update(int k){//核心1号
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
e[i][j]=e[i][k]+e[k][j]<e[i][j]?e[i][k]+e[k][j]:e[i][j];
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i){
scanf("%d",&t[i]);
}
inite();
for(int i=0;i<m;++i){
scanf("%d%d%d",&u,&v,&w);
e[u][v]=e[v][u]=w;
}
scanf("%d",&Q);
for(int i=0;i<Q;++i){
scanf("%d%d%d",&x,&y,&qt);
while(t[now]<=qt&&now<n){//核心2号
update(now);
now++;
}
if(t[x]>qt||t[y]>qt||e[x][y]==MAX)cout<<-1<<endl;
else cout<<e[x][y]<<endl;
}
return 0;
}