P1119 災後重建

2022-02-17 14:18:08 字數 1990 閱讀 3045

我覺得這個題出的很好,讓我對floyd 演算法有了乙個更深的理解。

floyd 是乙個求多源最短路徑的演算法,演算法的內容很簡單。

這個演算法的主要思路,就是通過其他的點進行中轉來求的兩點之間的最短路。因為我們知道,兩點之間有多條路,如果換一條路可以縮短距離的話,就更新最短距離。而它最本質的思想,就是用其他的點進行中轉,從而達到求出最短路的目的。

\(f[i][j][k]\) 表示從 \(i\) 到 \(j\) 只經過編號為 \(1\)~\(k\) 的節點的最短路。

轉移的話需要考慮兩種情況:最短路經過 \(k\) 和最短路不經過 \(k\),那麼就可以寫出轉移方程:

\(f[i][j][k]=\min(f[i][j][k-1],f[i][k][k-1]+f[k][j][k-1])\)

但是三維的狀態我們無法接受,需要考慮優化。

由於 \(k\) 是由 \(k-1\) 轉移來的,所以我們可以在外層列舉 \(k\),這樣就可以省掉第三維的狀態。

核心 \(code:\)

for(int k=1;k<=n;k++)         //在最外層列舉中轉點k 

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

if(i!=k&&i!=j)

f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

雖然 floyd 演算法的**很簡單,但是它的本質思想還是很重要的,而此題恰恰巧妙地考查了這點。

再回來看這個題,這個題就是讓我們求多次詢問的最短路,每次詢問都會有一些點無法經過。

如果我們對於每次詢問都跑一次 floyd 演算法的話時間複雜度肯定是**的,這就提醒我們可以離線操作。

注意到對於每次詢問給出的時間 \(t\),我們需要在 \(t[i]<=t\) 的所有點中跑最短路。換句話說,我們需要求 \(f[i][j][t]\) 表示從 \(i\) 到 \(j\) 只經過 \(t<=t\) 的點的最短路。

發現這和 floyd 演算法的本質思想一致,那麼我們就可以順水推舟地往下做了:

我們按照每個點的時間 \(t\) 來從小到大列舉 \(k\),這樣每次內層迴圈結束後我們就會更新所有 \(t<=t[k]\) 的點之間的最短路。

然後我們每處理完乙個中轉點 \(k\) 之後就看看能否回答一些詢問,能回答就輸出。由於題目中保證 \(t\) 是遞增的,所以我們只要讀入+儲存就好了,不必再按照時間排序了。

此題我們用鄰接矩陣來存圖,一定要處理好初始化的問題。

#include#include#includeusing namespace std;

const int n=205;

const int inf=1e9;

int read() //讀入優化

while(ch>='0'&&ch<='9')

return a*x;

}int n,m,top=1;

int t[n],id[n],f[n][n];

struct node //記錄每個詢問的資訊

a[1000000];

bool cmp(int x,int y) //按照t從小到大排序

for(int i=0;i=inf) printf("-1\n"); //無解的情況

else printf("%d\n",f[u][v]);

top++;

}return 0;

}

P1119 災後重建

原題鏈結 一開始直接想跑最短路 看了看詢問次數 放棄了 然後果斷看了題解 floyd 用啥都不會用它的好嗎 平常的最劣選擇 但是 它就是正解 floyd的原理 就是列舉中點 這裡 因為出題人 已經把詢問排好了序 只需要判斷中點 有沒有重建完成 把它加入圖中 include include inclu...

P1119 災後重建

b地區在 過後,所有村莊都造成了一定的損毀,而這場 卻沒對公路造成什麼影響。但是在村莊重建好之前,所有與未重建完成的村莊的公路均無法通車。換句話說,只有連線著兩個重建完成的村莊的公路才能通車,只能到達重建完成的村莊。給出b地區的村莊數n,村莊編號從0到n 1,和所有m條公路的長度,公路是雙向的。並給...

P1119 災後重建

b 地區在 過後,所有村莊都造成了一定的損毀,而這場 卻沒對公路造成什麼影響。但是在村莊重建好之前,所有與未重建完成的村莊的公路均無法通車。換句話說,只有連線著兩個重建完成的村莊的公路才能通車,只能到達重建完成的村莊。給出 b 地區的村莊數 n 村莊編號從 0 到 n 1 和所有 m 條公路的長度,...