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

Floyd多源最短路算法

学习濒死的SPFA与堆优的Dijkstra之后Floyd就被我们无情地忘记了。

算法简介

Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。

虽然复杂度很高(时间复杂度 \(O(n^3)\),空间复杂度 \(O(n^2)\) ),但是却很容易写,很容易处理一些对时间空间限制不大的毒瘤图上多源/单源最短路问题。

实现过程

  • 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。

  • 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。

  • 把图用邻接矩阵 \(G\) 表示出来,如果从 \(V_i\)\(V_j\) 有路可达,则 \(G[i][j]=d\)\(d\) 表示该路的长度;否则\(G[i][j]=\infty\)。定义一个矩阵D用来记录所插入点的信息,D[i][j]表示从Vi到Vj需要经过的点,初始化\(D[i][j]=j\)。把各个顶点插入图中,比较插点后的距离与原来的距离, \(G[i][j] = min\{G[i][j], G[i][k]+G[k][j]\}\) ,如果 \(G[i][j]\) 的值变小,则 \(D[i][j]=k\) 。在 \(G\) 中包含有两点之间最短道路的信息,而在 \(D\) 中则包含了最短通路径的信息。

  • 比如,要寻找从 \(V_5\)\(V_1\) 的路径。根据 \(D\) ,假如\(D(5,1)=3\) 则说明从\(V_5\)\(V_1\) 经过 \(V_3\) ,路径为\({V_5,V_3,V_1}\),如果 \(D(5,3)=3\),说明 \(V_5\)\(V_3\) 直接相连,如果 \(D(3,1)=1\) ,说明 \(V_3\)\(V_1\) 直接相连。

实现代码

for (k = 1; k <= n; k++) {
  for (x = 1; x <= n; x++) {
    for (y = 1; y <= n; y++) {
      f[k][x][y] = min(f[k - 1][x][y], f[k - 1][x][k] + f[k - 1][k][y]);
    }
  }
}

本题分析

要枚举当前可以访问到的节点(村庄)进行遍历,参考代码如下:

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

const int MAXN = 205;
const int inf = 0x3f3f3f3f;

int edge[MAXN][MAXN],times[MAXN];
int n,m,q;
int x,y,v,t;
int cur;

int main(){
    memset(edge,0x3f,sizeof(edge));
    cin>>n>>m;
    for(int i=0;i<n;i++) {
        edge[i][i]=0;
    }
    for(int i=0;i<n;i++) {
        cin>>times[i];
    }
    for(int i=0;i<m;i++) {
        cin>>x>>y>>v;
        edge[x][y]=edge[y][x]=v;
    }
    cin>>q;
    while(q--){
        cin>>x>>y>>t;
        while(times[cur]<=t&&cur<n) {
            for(int i=0;i<n;i++) {
                for(int j=0;j<n;j++) {
                    edge[i][j]=min(edge[i][j],edge[i][cur]+edge[cur][j]);
                }
            }
            cur++;
        }
        if(times[x]>t||times[y]>t||edge[x][y]>=inf) {
            cout<<-1<<endl;
        } 
        else {
            cout<<edge[x][y]<<endl;
        }
    }
}

鸣谢

本文参考资料如下:

posted @ 2022-01-26 15:15  蒟蒻xiezheyuan  阅读(47)  评论(0编辑  收藏  举报