洛谷 P1119 灾后重建

原题链接:

灾后重建 - 洛谷

题目大意:

有n个村庄,村庄间有m条公路,一次地震将连向每个村庄的公路损坏,所以要进行维修,数据保证编号小的村庄维修时间更少,编号大的村庄维修时间更多。后面有q个询问,问是在第几天的时候从x村庄到y村庄我们是否能到达,如果能到达的最小距离是多少

解题思路:

题目数据给出的是一个稠密图,最大可以是一个全连通图,每两个点之间都有边直连。

最朴素的想法是每读一个查询就对整个新图重新跑一次dijkstra,复杂为O(Q*m*n*logn)=O(Q*n^3*logn),如果换成floyd,复杂度为O(Q*n^3),更好,但依然超时。

其实我们可以只需要完整的进行一次floyd,而不需要Q次,总的复杂度为O(Q+n^3)。

因为每个顶点的修复时间是按顶点编号递增的,floyd的执行过程又是逐步加入点,从第1个点扩展到第n个点,加第k个点时,前面的1~k-1个点都已经执行完毕,当k到达最后的第n个点时,包含所有1~n点的最短路径计算完毕。而在本题,在第t个时刻,小于t时刻的图的顶点的最短路径计算完毕,在t+1时刻,只需要在t时刻计算结果的基础上,继续计算新加入的点(t~t+1时间内修复的村庄)即可。

代码(CPP):

#include 
using namespace std;
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 310;
const int INF = 0x3fffffff;
const int mod = 1000000007;
struct edge {
    int v, w;
};
int dis[maxn][maxn];
int t[maxn];
int n, m;

/*
    题目数据给出的是一个稠密图,最大可以是一个全连通图,每两个点之间都有边直连。
    最朴素的想法是每读一个查询就对整个新图重新跑一次dijkstra,复杂度为O(Q*m*n*logn)=O(Q*n^3*logn),
    如果换成floyd,复杂度为O(Q*n^3),更好,但依然超时。
    其实我们可以只需要完整的进行一次floyd,而不需要Q次,总的复杂度为O(Q+n^3)。
    因为每个顶点的修复时间是按顶点编号递增的,floyd的执行过程又是逐步加入点,从第1个点扩展到第n个点,
    加第k个点时,前面的1~k-1个点都已经执行完毕,当k到达最后的第n个点时,包含所有1~n点的最短路径计算完毕。
    而在本题,在第t个时刻,小于t时刻的图的顶点的最短路径计算完毕,在t+1时刻,只需要在t时刻计算结果的基础上,
    继续计算新加入的点(t~t+1时间内修复的村庄)即可。
*/

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> t[i];
    }
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++) {
            if (i != j)
                dis[i][j] = dis[j][i] = INF;
        }
    }
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        dis[u][v] = dis[v][u] = w;
    }

    int q;
    cin >> q;
    int k = 0;
    while (q--) {
        int u, v, tt;
        cin >> u >> v >> tt;
        for (; k < n && t[k] <= tt; k++) {  // 将t~t+1时刻内修复的点的计算出来
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dis[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j]) {
                        dis[i][j] = dis[i][k] + dis[k][j];
                    }
                }
            }
        }
        if (dis[u][v] == INF || t[u] > tt || t[v] > tt) {
            cout << -1 << endl;
        } else {
            cout << dis[u][v] << endl;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout << fixed;
    cout.precision(18);

    solve();
    return 0;
}

你可能感兴趣的:(算法&数据结构,算法竞赛,算法,数据结构)