灾后重建

传送门

这道题一开始的暴力做法就是直接模拟之后用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;
}

 

posted @ 2018-09-07 16:27  CaptainLi  阅读(193)  评论(0编辑  收藏  举报