1. 程式人生 > 實用技巧 >P1119 災後重建 題解

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的理解!

完結撒花