洛谷

洛谷题单指南-最短路-P1119 灾后重建

原题链接:https://www.luogu.com.cn/problem/P1119

题意解读:n个节点的图,节点按时间依次可用,多个询问求某一时刻两点之间的最短路。

解题思路:

节点数只有200,很快能想到Floyd算法,但是如果只背过Floyd的模版,是不足以解决这道题,需要对Floyd算法的本质有深刻理解。

Floyd算法是基于动态规划的思想,先在一个小范围的节点中计算任意两点的最短路,再逐步扩大节点范围。

状态表示:可以设d[i][j][k]表示从i只经过1~k的节点走到j的最短路

状态转移:按照是否经过k分成两种情况,取最小值,d[i][j][k] = min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])

初始化:d[u][v][0] = w

根据以上的分析,这道题要求某个时刻两点的最短路,而某个时刻有多少点可以用,也是可以计算出来的:

设每个点恢复可用的时间用t[i]保存,为了便于floyd计算,节点我们都加1处理,范围由0 ~ n-1变成1 ~ n

这样给定一个时刻,有多少点可以用,通过int x = upper_bound(t + 1, t + n + 1, time) - t - 1即可计算出来最大的可用节点

直接根据状态表示的定义,即可得到这一时刻u->v的最短路是d[u][v][x]。

注意:

1、如果查询的时刻最大的可用节点x均比u或者v小,那么u或者v是不可达的,要输出-1。

2、如果d[u][v][x] = INF,表示u不能走到v,也要输出-1。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 205, Q = 50005, INF = 0x3f3f3f3f;
int d[N][N][N];
int t[N];
int n, m, q;

int main()
{
    cin >> n >> m;
    memset(d, 0x3f, sizeof(d));
    for(int i = 1; i <= n; i++) cin >> t[i];
    while(m--)
    {
        int u, v, w;
        cin >> u >> v >> w;
        u++; v++;
        d[u][v][0] = d[v][u][0] = w;
    }

    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                d[i][j][k] = min(d[i][k][k - 1] + d[k][j][k - 1], d[i][j][k - 1]);

    cin >> q;
    for(int i = 0; i < q; i++) 
    {
        int u, v, time;
        cin >> u >> v >> time;
        u++; v++;
        int x = upper_bound(t + 1, t + n + 1, time) - t - 1;
        if(u > x || v > x || d[u][v][x] == INF)
        {
            cout << -1 << endl;
        }
        else cout << d[u][v][x] << endl;
    }

    return 0;
}