P1119 灾后重建-省选floyd!!!

版权声明:看我干嘛? 你又没打算转载我的博客~ https://blog.csdn.net/wjh2622075127/article/details/82716184
P1119 灾后重建

题目背景

B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。

题目描述

给出 B 地区的村庄数 N ,村庄编号从 0 N 1 ,和所有 M 条公路的长度,公路是双向的。并给出第 i 个村庄重建完成的时间 t i ,你可以认为是同时开始重建并在第 t i 天重建完成,并且在当天即可通车。若 t i 0 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 Q 个询问 ( x , y , t ) ,对于每个询问你要回答在第 t 天,从村庄 x 到村庄y的最短路径长度为多少。如果无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x 或村庄 y 在第t天仍未重建完成 ,则需要返回 1

输入输出格式

输入格式:

第一行包含两个正整数 N , M ,表示了村庄的数目与公路的数量。

第二行包含 N 个非负整数 t 0 , t 1 , , t N 1 ,表示了每个村庄重建完成的时间,数据保证了 t 0 t 1 t N 1

接下来 M 行,每行 3 个非负整数 i , j , w w 为不超过 10000 的正整数,表示了有一条连接村庄 i 与村庄 j 的道路,长度为 w ,保证 i j ,且对于任意一对村庄只会存在一条道路。

接下来一行也就是 M + 3 行包含一个正整数 Q ,表示 Q 个询问。

接下来 Q 行,每行 3 个非负整数 x , y , t ,询问在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少,数据保证了 t 是不下降的。

输出格式:

Q 行,对每一个询问 ( x , y , t ) 输出对应的答案,即在第 t 天,从村庄 x 到村庄 y 的最短路径长度为多少。如果在第t天无法找到从 x 村庄到 y 村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄 y 在第 t 天仍未修复完成,则输出 1

输入输出样例

输入样例#1: 复制
4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4
输出样例#1: 复制
-1
-1
5
4

说明

对于 30 % 的数据,有 N 50

对于 30 % 的数据,有 t i = 0 ,其中有 20 % 的数据有 t i = 0 N>50

对于 50 % 的数据,有 Q 100

对于 100 % 的数据,有 N 200 M N × ( N 1 ) / 2 Q 50000 ,所有输入数据涉及整数均不超过 100000



此题居然是一道floyd题目!一直以为求最短路径的算法里面floyd是最简单最直接的,但是由于它是求多源最短路径的算法,所以平时用得非常少,似乎也只觉得它只在那种直接告诉你求多源最短路径的题目里出现。但这道题可以说改变了我对floyd的看法,floyd再不是从前的floyd了,而是现在的floyd。。。

首先这是一道比较动态的最短路问题,结点在某个时间才会出现,因此和它相连的边也才会出现。用最朴素的想法,对每次查询,都对其进行一次dijkstra求最短路,于是时间复杂度为O(n²Q), 当然会超时了,只过了6个点,60分。

此题正解是floyd,你可能会想,单源O(n²)的复杂度都解决不了,你个多源O(n³)的来凑什么热闹。但其实这道题,简直就是为floyd算法所设计的!弄懂这道题,将会对floyd有新的看法。

其实从题意还是可以看出floyd的苗头的,比如,每次查询的起点和终点都不是固定的,那么这最短路其实是求的多源而非单源,单源的话,由于每次都可能换起点,那么每次都要推翻原来的最短路,极难优化。

而多源的floyd,它的思想是,如果以k为中转点,能不能更新点i到点j的距离,能则更新。如果这个k点暂时不能访问呢?这就意味着此时k不能作为中转点。但这并不影响其他点与点之间最短路的求解,因为k这时不能访问,也即不能作为中转点,那么就不以他为中转点来计算,等到后面他可以作为中转点的时候再计算。floyd中转点计算是不分顺序的。

那么,每个点从不能访问到能访问,只有一次,这就意味着我们只需要对每个点进行一次中转计算,那么时间复杂度就是O(n³+Q), perfect!

我们应该抓取的特征是:这张图所有的边都给出了,而结点是一个一个冒出来的,且查询是多源最短路径。这么多符合floyd得特征,怎么不用floyd?floyd也可以很动态的好不好。

100分代码

#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 205;
int q, s = 0, tim[maxn];
int G[maxn][maxn] = {};
bool ok[maxn] = {}, vis[maxn] = {};

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> tim[i];
    }
    memset(G, 0x3f, sizeof(G));
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        G[u][v] = G[v][u] = w;
    }
    cin >> q;
    for (int i = 0; i < q; ++i) {
        int u, v, t;
        cin >> u >> v >> t;
        while (tim[s] <= t) {
            ok[s] = true;
            s++;
        }
        for (int k = 0; k < n; ++k) {
            if (ok[k] && !vis[k]) {
                vis[k] = true;
                for (int i = 0; i < n; ++i) {
                    for (int j = 0; j < n; ++j) {
                        G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
                    }
                }
            }
        }
        if (ok[u] && ok[v] && G[u][v] != 0x3f3f3f3f) cout << G[u][v] << endl;
        else cout << -1 << endl;
    }
}

60分朴素代码(其实难写多了)

#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;

struct Edge {
    int to, w, next;
};

const int maxn = 205, maxm = 20005;
const int INF = 0x3f3f3f3f;
int n, m, cnt = 1;
int head[maxn] = {}, ok[maxn] = {};
int vis[maxn] = {}, dis[maxn] = {};
pair<int, int> Time[maxn];
Edge edge[maxm];

void add(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void dijkstra(int start, int end)
{
    dis[start] = 0;
    for (int i = 0; i < n; ++i) {
        int u = -1, _min = INF;
        for (int j = 0; j < n; ++j) {
            if (!vis[j] && _min > dis[j]) {
                u = j;
                _min = dis[j];
            }
        }
        if (u == -1) return;
        vis[u] = true;
        for (int j = head[u]; j; j = edge[j].next) {
            int v = edge[j].to, w = edge[j].w;
            if (ok[v] && !vis[v]) {
                dis[v] = min(dis[v], dis[u] + w);
            }
        }
    }
    return;
}

int main()
{
    std::ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> Time[i].first;
        Time[i].second = i;
    }
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }
    int q;
    cin >> q;
    int s = 0;
    for (int i = 0; i < q; ++i) {
        int u, v, t;
        cin >> u >> v >> t;
        while (Time[s].first <= t && s < n) {
            ok[Time[s].second] = true;
            s++;
        }
        memset(dis, 0x3f, sizeof(dis));
        memset(vis, false, sizeof(vis));
        if (!(ok[u] && ok[v])) cout << -1 << endl;
        else {
            dijkstra(u, v);
            cout << (dis[v] == INF ? -1 : dis[v]) << endl;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/82716184
今日推荐