【2020.2.7 练习赛】 T5-道路开通/灾后重建(图论-Floyd)

本文通过一道灾后重建的题目介绍了如何利用Floyd算法解决公路开通后的最短路径问题。在疫情背景下,ZJ省部分城市公路逐步恢复,给定每个城市的开通时间及询问,需要确定在特定日期内能否从一个城市到达另一个城市并返回最短路径。通过Floyd算法的优化,使用vis数组记录中转点,解决了时间限制问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来源:JZOJ,Luogu P1119 灾后重建

题目描述-原题

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

改版

疫情导致很多城市的公路都封掉了。

现在疫情开始没那么严峻, z j zj zj 省的高速公路开始慢慢恢复。

现在设 z j zj zj 省的城市有 N N N 个,有 M M M 条双向公路相连,每条公路有相应的长度。

现在给出第 i i i 个城市公路开通的时间 t [ i ] t[i] t[i]

当然如果 t [ i ] t[i] t[i] 0 0 0,表示第i个城市就没有封锁。

现在指挥部要知道道路运行情况,有 S S S 次询问,询问格式为 ( s t , e n , q ) (st,en,q) stenq

每个询问表示从 s t st st 号城市到 e n en en 城市,在第 q q q 天是否可以到达,如果可以到达,请给出最短的长度,如果不可以到达,输出 − 1 -1 1

解题思路

  • 这是一道深入 F l o y d Floyd Floyd 本质的题目,首先得先了解一下 F l o y d Floyd Floyd,这短小精悍的算法:通过其他中转点求任意两点之间的最短路,丢代码;
for(k=1;k<=n;k++)
  for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
      dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
  • 这段代码能否和本题联系起来呢?
  • 思路如下:按时间顺序找到每一个在 q q q天之内能修好的点,作为中转点,暴力 F l o y d Floyd Floyd 即可
  • 不过,这样是会 T L E TLE TLE 的,我们需要加一个小优化,就是用一个 v i s vis vis 数组标记, v i s [ i ] vis[i] vis[i] 表示 i i i 是否做过中转点,这样就可以 A C AC AC

C o d e Code Code

#pragma GCC optimize ("O2")  //err... 手动开O2...罒ω罒
#pragma G++ optimize ("O2") 
#include <bits/stdc++.h>
#define Inf 0x3fffffff
using namespace std;
bool vis[210];
int a[210][210],t[210];
inline int read()  //快读
{
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9') {if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
int main()
{
	freopen("rebuild.in","r",stdin);
	freopen("rebuild.out","w",stdout);
	int n=read(),m=read();
	for (register int i=0;i<n;i++) t[i]=read();
	for (register int i=0;i<n;i++)  //手动赋值正无穷(邻接矩阵)
     for (register int j=0;j<n;j++)
      a[i][j]=Inf;
    for (register int i=0;i<n;i++) a[i][i]=0;  //i点到i点距离为0
	for (register int i=1;i<=m;i++)
	{
		int x=read(),y=read(),v=read();
		a[x][y]=a[y][x]=v;  //赋初值
	}
	int q;
	q=read();
	for (register int I=1;I<=q;I++)  //q次询问
	{
		int st=read(),en=read(),time=read();
		int k=0;
		for (register int k=0;k<n;k++)
		{
			if (t[k]<=time && !vis[k])  //找到没有用过且在q天之内能修好的点
			{
				vis[k]=1;  //标记
				for (register int i=0;i<n;i++)
		  	 	 for (register int j=0;j<n;j++)
		  	  	  a[i][j]=min(a[i][j],a[i][k]+a[k][j]);  //Floyd更新距离
			}
		}
		if (t[st]>time || t[en]>time || a[st][en]>=Inf) printf("-1\n");  //不合法
		else printf("%d\n",a[st][en]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值