P1119 灾后重建 题解

前置芝士:

我的Floyd基础知识讲解


说白了,Floyd本质上就是一个dp:

K为外层状态,i和j为内层枚举。Floyd完整状态其实是:\(f[k][i][j]\)表示以前K个点为转移点(即编号为1~k的点可以被用来松弛),从\(i\)\(j\)的最短路径。

每条最短路径都是以k为基础的,对于未枚举进k的点将不被考虑进最短路径的松弛,k也就作为整个图的最短路径的前提条件,那么自然作为最外层状态转移。

考虑这道题:

给你一张无向图,给你每一个节点的信息t表示当前节点在时间为t以及以后可以到达。

翻译一下,也就是可以考虑进最短路的松弛。

朴素想法,对于每个t,我们可以枚举每一个结点,如果结点的\(t>\)当前询问的时间,将不被考虑进图中。每一次询问在重新建完图后跑一边Floyd,并在判断起点村庄s到终点l能否到达,如果能到达输出即可。

很明显,T的死死的。每次跑一遍floyd实在是太浪费了时间,而且题目中的数据是递增的。出题人给你的优化条件你怎么能不用呢。

k表示考虑了1~n的节点作为可松弛点,也就是说:编号1到n的节点都能够到达。

换句话说,1到n节点满足了被加入最短路集合的条件。

对于这道题,从暴力思路可以看出,一个节点被加入最短路集合的条件就是当前节点的t值小于等于询问的时间t。而出题人给你的t是单调的。那么,我们设上一次询问的时间为\(t_{0}\),这一次询问的时间为\(t_{1}\),以t为floyd最外层状态k,对于\(t_{0}\)之前的k我们就不必再跑一遍了,从上一次能够到达的编号为now的节点开始往后判断节点的t是否小于询问的t,直到大于询问的t为止。

原因很好理解,节点的t随着编号的上升而上升,而询问的t又上升,那么考虑过的节点一定满足加入最短路集合的条件,也就不用再跑一遍Floyd(\(n^2\))的部分了。

核心代码:

q=read();
	while(q--)
	{
		u=read(),v=read(),w=read();
		while(now<n&&a[now]<=w)
		{
			dp(now);//now节点就可以被加入可用来松弛的节点的集合,跑一边floyd
			now++;//考虑编号更大的,t值更大的结点。
		}
	}
	\\Floyd部分
inline void dp(int now)
{
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			dis[i][j]=min(dis[i][j],dis[i][now]+dis[now][j]);
		}
}

全部代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
#define re register
using namespace std;
const int N=201;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m,a[N],u,w,v,q,judge=-1,dis[N][N],now;
inline void add(int from,int to,int w)
{
	dis[from][to]=dis[to][from]=w;
}
inline void dp(int now)
{
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			dis[i][j]=min(dis[i][j],dis[i][now]+dis[now][j]);
		}
}
int main()
{
	n=read(),m=read();
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			dis[i][j]=1e9+7;
	for(int i=0;i<n;i++)
	{
		a[i]=read();
	}
	for(int i=1;i<=m;i++)
	{
		u=read(),v=read(),w=read();
		add(u,v,w);
	}
	q=read();
	while(q--)
	{
		u=read(),v=read(),w=read();
		while(now<n&&a[now]<=w)
		{
			dp(now);
			now++;
		}
		if(a[u]>w||a[v]>w)printf("%d\n",judge);
			else
			{
				if(dis[u][v]==1e9+7)printf("%d\n",judge);
				else printf("%d\n",dis[u][v]);
			}
	}
	return 0;
}

希望看完这篇博文后能够加深各位对Floyd的理解!

完结撒花

posted @ 2020-07-21 20:33  李白莘莘学子  阅读(253)  评论(0编辑  收藏  举报