灾后重建
这道题一开始的暴力做法就是直接模拟之后用dij跑最短路。其实它的极限值还没到1e8不过有常数你还是会超时。
上面的暴力能得60分。
之后我们考虑正解,因为数据范围是200所以我们可以直接跑Floyd。不过问题是我们怎么在一张不是完整的图上更新距离。
Floyd算法的本质是动态规划,其转移方程 为:f[k][i][j] = min( f[k-1][i][j], f([k-1][i] [k])+f[k-1][k][j] )。
f[k][i][j]表示路径除开起点i与终点j,只经过前k个点中的某些 点,从i到j的最小值。计算这个值只需要考虑两种情况:最短路经 过k,和最短路不经过k(那么就经过前k-1个点中的某些点)。由于 k要从k-1转移而来,自然k为最外层的循环。而经过滚动(类似于背 包问题)后,就变成了我们熟悉的f[i][j]=min(f[i][j],f[i] [k]+f[k][j])了。
仔细思考之后发现……其实你每加入一个新的点,你所能更新的点对之间的距离一定是与这个点有关的……其实就是一个Floyd 的过程!把当前加入的这个点当做中间点k,枚举i,j就可以了。
这样时间复杂度是On^3,可以过。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define pr pair<int,int> #define mp make_pair #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 50005; const int INF = 0x3fffffff; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct edge { int next,to,v; }e[M<<1]; int n,m,t[205],x,y,z,ecnt,head[205],qu,h = 0,dis[205][205],day[M],fr[M],st[M]; bool pd[205]; int main() { n = read(),m = read(); rep(i,0,n-1) rep(j,0,n-1) if(i^j) dis[i][j] = INF; rep(i,0,n-1) t[i] = read(); rep(i,1,m) { x = read(),y = read(),z = read(); dis[x][y] = dis[y][x] = z; } qu = read(); rep(i,1,qu) { x = read(),y = read(),z = read(); while(t[h] <= z && h < n) { pd[h] = 1; rep(i,0,n-1) rep(j,0,n-1) dis[i][j] = min(dis[i][j],dis[i][h] + dis[h][j]); h++; } if(dis[x][y] == INF || !pd[x] || !pd[y]) printf("-1\n"); else printf("%d\n",dis[x][y]); } return 0; }
当你意识到,每个上一秒都成为永恒。