https://www.luogu.org/problem/P1119
题目背景
B
B
B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
题目描述
给出
B
B
B地区的村庄数
N
N
N,村庄编号从
0
0
0到
N
−
1
N−1
N−1,和所有
M
M
M条公路的长度,公路是双向的。并给出第
i
i
i个村庄重建完成的时间
t
i
t_{i}
ti,你可以认为是同时开始重建并在第
t
i
t_{i}
ti天重建完成,并且在当天即可通车。若
t
i
t_{i}
ti为
0
0
0则说明地震未对此地区造成损坏,一开始就可以通车。之后有
Q
Q
Q个询问
(
x
,
y
,
t
)
(x,y,t)
(x,y,t),对于每个询问你要回答在第
t
t
t天,从村庄
x
x
x到村庄
y
y
y的最短路径长度为多少。如果无法找到从
x
x
x村庄到
y
y
y村庄的路径,经过若干个已重建完成的村庄,或者村庄
x
x
x或村庄
y
y
y在第
t
t
t天仍未重建完成 ,则需要返回
−
1
−1
−1。
输入格式
第一行包含两个正整数
N
,
M
N,M
N,M,表示了村庄的数目与公路的数量。
第二行包含
N
N
N个非负整数
t
0
,
t
1
,
…
…
,
t
n
−
1
t_{0},t_{1},……,t_{n-1}
t0,t1,……,tn−1,表示了每个村庄重建完成的时间,数据保证了
t
0
<
=
t
1
<
=
…
…
<
=
t
n
−
1
t_{0}<=t_{1}<=……<=t_{n-1}
t0<=t1<=……<=tn−1。
接下来
M
M
M行,每行
3
3
3个非负整数
i
,
j
,
w
i,j,w
i,j,w,
w
w
w为不超过10000的正整数,表示了有一条连接村庄
i
i
i与村庄
j
j
j的道路,长度为
w
w
w,保证
i
≠
j
i≠j
i=j,且对于任意一对村庄只会存在一条道路。
接下来一行也就是
M
+
3
M+3
M+3行包含一个正整数
Q
Q
Q,表示
Q
Q
Q个询问。
接下来
Q
Q
Q行,每行
3
3
3个非负整数
x
,
y
,
t
x,y,t
x,y,t,询问在第
t
t
t天,从村庄
x
x
x到村庄
y
y
y的最短路径长度为多少,数据保证了
t
t
t是不下降的。
输出格式
共
Q
Q
Q行,对每一个询问
(
x
,
y
,
t
)
(x,y,t)
(x,y,t)输出对应的答案,即在第
t
t
t天,从村庄
x
x
x到村庄
y
y
y的最短路径长度为多少。如果在第
t
t
t天无法找到从
x
x
x村庄到
y
y
y村庄的路径,经过若干个已重建完成的村庄,或者村庄
x
x
x或村庄
y
y
y在第
t
t
t天仍未修复完成,则输出
−
1
−1
−1。
思路:非常好的题目,涉及到 f l o y d floyd floyd算法的本质。 f l o y d floyd floyd算法的核心思想:通过其他的点进行中转来求两点之间的最短路。
void floyd()
{
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
s[i][j]=min(s[i][j],s[i][k]+s[k][j]);
}
这段代码的基本思想就是:最开始只允许经过
1
1
1号顶点进行中转,接下来只允许经过
1
1
1和
2
2
2号顶点进行中转……允许经过
[
1
,
n
]
[1,n]
[1,n]号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从
i
i
i号顶点到
j
j
j号顶点只经过前
k
k
k号点的最短路程或者就是从
i
i
i直接到
j
j
j的路程。讲到这里再看一下题目,就不难将两者联系起来了,直接跑一次
f
l
o
y
d
floyd
floyd,然后对于某个查询,若其对应的时间下前
i
i
i个村庄都已经建立好了,那么就看在第
i
i
i次更新后
u
u
u和
v
v
v的最短距离(当然
u
u
u和
v
v
v都要
<
=
i
<=i
<=i)。
代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pr pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=205;
int t[maxn];
int dp[maxn][maxn][maxn];
int n,m,q;
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[k][i][j]=min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&t[i]);
int u,v,dis;
memset(dp,INF,sizeof(dp));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][j]=0;
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&u,&v,&dis);
++u,++v;
dp[0][u][v]=dp[0][v][u]=dis;
}
floyd();
scanf("%d",&q);
int tmp;
while(q--)
{
scanf("%d %d %d",&u,&v,&dis);
++u,++v;
tmp=upper_bound(t+1,t+1+n,dis)-t-1;
if(u>tmp||v>tmp||dp[tmp][u][v]==INF)
printf("-1\n");
else
printf("%d\n",dp[tmp][u][v]);
}
return 0;
}