P1119 災後重建 題解
阿新 • • 發佈:2020-07-21
前置芝士:
說白了,Floyd本質上就是一個dp:
K為外層狀態,i和j為內層列舉。Floyd完整狀態其實是:\(f[k][i][j]\)表示以前K個點為轉移點(即編號為1~k的點可以被用來鬆弛),從\(i\)到\(j\)的最短路徑。
每條最短路徑都是以k為基礎的,對於未列舉進k的點將不被考慮進最短路徑的鬆弛,k也就作為整個圖的最短路徑的前提條件,那麼自然作為最外層狀態轉移。
考慮這道題:
給你一張無向圖,給你每一個節點的資訊t表示當前節點在時間為t以及以後可以到達。
翻譯一下,也就是可以考慮進最短路的鬆弛。
樸素想法,對於每個t,我們可以列舉每一個結點,如果結點的\(t>\)
很明顯,T的死死的。每次跑一遍floyd實在是太浪費了時間,而且題目中的資料是遞增的。出題人給你的優化條件你怎麼能不用呢。
k表示考慮了1~n的節點作為可鬆弛點,也就是說:編號1到n的節點都能夠到達。
換句話說,1到n節點滿足了被加入最短路集合的條件。
對於這道題,從暴力思路可以看出,一個節點被加入最短路集合的條件就是當前節點的t值小於等於詢問的時間t。而出題人給你的t是單調的。那麼,我們設上一次詢問的時間為\(t_{0}\),這一次詢問的時間為\(t_{1}\)
原因很好理解,節點的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的理解!
完結撒花