floyd 算法——P1119 灾后重建

floyd 算法

是图论中较为简单的最短路算法,但在某些方面远超最短路范围。

算法思路

定义 \(f[x][y]\)\(x\)\(y\) 节点的最短路径。

初始化:若存在边 \((x,y)\)\(f[x][y]\) 等于边长度;若不存在,为 \(+\infty\)

特别的,\(f[x][x]=0\)

我们考虑一下,\(x,y\) 这两个节点通过节点 \(k\) 来更新最短路径。

那么有 \(f[x][y]=\min(f[x][k]+f[k][y],f[x][y])\)

比较好理解,就是到 \(k\) 的两条路径相加。

这时可能 \(f\) 数组还不是最短路径,但无伤大雅,后面我们会更新到。

比较草率,上代码吧。

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

例题

P1119 灾后重建

思路

floyd 第一层 \(k\) 的循环的含义是通过前 \(k\) 个点可以转移到的最短路径。

我们将点按修复时间排序即可。

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

const int maxm=1e5+5,maxn=205;

int n,m,q;
int dis[maxn][maxn],T[maxn];

pair<int,int>vex[maxm];

int now=0,k=1;
void fly()
{
    for(;vex[k].first<=now&&k<=n;k++)
    {
        int K=vex[k].second;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dis[i][K]+dis[j][K]<dis[i][j]) dis[i][j]=dis[j][i]=dis[i][K]+dis[j][K];
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dis[i][j]=1e9;
    for(int i=1;i<=n;i++) scanf("%d",&vex[i].first),vex[i].second=i,dis[i][i]=0,T[i]=vex[i].first;
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        x++,y++;
        dis[x][y]=dis[y][x]=z;
    }

    sort(vex+1,vex+n+1);
    fly();
    scanf("%d",&q);
    while(q--)
    {
        int x,y,t;
        scanf("%d%d%d",&x,&y,&t);
        x++,y++;
        if(T[x]>t||T[y]>t){printf("-1\n");continue;}
        if(now<t)
        {
            now=t;
            fly();
        }
        if(dis[x][y]==dis[0][0]) printf("-1\n");
        else printf("%d\n",dis[x][y]);
    }
}
posted @ 2024-01-21 12:34  彬彬冰激凌  阅读(15)  评论(0编辑  收藏  举报