图论 —— 最短路 —— Floyd 算法
【概述】?
Floyd 算法又稱為插點(diǎn)法,是一種用于尋找給定的加權(quán)圖中多源點(diǎn)之間最短路徑的算法。
其最大特點(diǎn)是可以計(jì)算出現(xiàn)負(fù)邊權(quán)時(shí)的最短路,實(shí)際應(yīng)用中,很多題目不是問如何用 Floyd 求最短路,而是用 Floyd 的動(dòng)態(tài)規(guī)劃思想來解決類似 Floyd 的問題。
其時(shí)間復(fù)雜度是?O(N*N*N),N是頂點(diǎn)數(shù)。
【極大值的選擇】
設(shè)置無窮大時(shí),0x7fffffff 是 32-bit int 的最大值,如果這個(gè)無窮大只用于一般的比較,那么?0x7fffffff 是一個(gè)完美的選擇,但在更多情況下,其并不是一個(gè)好的選擇。
在最短路的松弛操作時(shí),如果 u、v 間無邊,那么 w[u][v]=INF,此時(shí)若 INF 取?0x7fffffff,那么 dis[u]+w[u][v] 會(huì)溢出而變成負(fù)數(shù),此時(shí)松弛操作便會(huì)出錯(cuò),準(zhǔn)確來說,0x7fffffff 不能滿足無窮大加一個(gè)有窮的數(shù)依然是無窮大,而是變成了一個(gè)很小的負(fù)數(shù)。
if(dis[u]+w[u][v]<dis[v]) dis[v]=dis[u]+w[u][v];由于要找一個(gè)能夠滿足無窮大加無窮大依然是無窮大的數(shù),因此,可以選用 0x3f3f3f3f
0x3f3f3f3f 的十進(jìn)制是 1061109567,是 10^9 級(jí)別的,與 0x7fffffff 一個(gè)數(shù)量級(jí),而一般場(chǎng)合下的數(shù)據(jù)都是小于10^9的,所以它可以作為無窮大使用而不致出現(xiàn)數(shù)據(jù)大于無窮大的情形。?
另一方面,由于一般的數(shù)據(jù)都不會(huì)大于 10^9,所以當(dāng)我們把無窮大加上一個(gè)數(shù)據(jù)時(shí),它并不會(huì)溢出,事實(shí)上 0x3f3f3f3f + 0x3f3f3f3f = 2122219134,這個(gè)數(shù)雖然非常大但卻沒有超過 32-bit int 的表示范圍,因此?0x3f3f3f3f 還滿足了無窮大加無窮大還是無窮大的需求。
此外,當(dāng)想將某個(gè)數(shù)組清零或全部賦值為 -1,通常會(huì)使用 memset() 函數(shù),但是當(dāng)想將某個(gè)數(shù)組全部賦值為無窮大時(shí),就不能使用memset 函數(shù)而是寫循環(huán)了,因?yàn)?memset 是按字節(jié)操作的,它能夠?qū)?shù)組清零是因?yàn)?0 的每個(gè)字節(jié)都是 0。但如果將無窮大設(shè)為 0x3f3f3f3f,由于其每個(gè)字節(jié)都是 0x3f,因此可以直接使用 memset() 函數(shù)來操作。
【算法核心】
1.初始化:
設(shè) dis[i][j] 為 i、j 兩點(diǎn)的距離,w[i][j] 為 i、j 兩點(diǎn)的權(quán)值。
若點(diǎn) u、v 有邊連接,則:dis[u][v]=w[u][v],即:初始化兩點(diǎn)最短距離為兩點(diǎn)權(quán)值。
若點(diǎn) u、v 無邊連接,則:dis[u][v]=0x3f3f3f3f,即:初始化為一極大值。
2.算法主體
for(int k=1;k<=n;k++)//第一重循環(huán)為i→j的中間點(diǎn)kfor(int i=1;i<=n;i++)//第二重循環(huán)為起點(diǎn)ifor(int j=1;j<=n;j++)//第三重循環(huán)為終點(diǎn)jif(dis[i][j]>dis[i][k]+dis[k][j])//如果i→k的距離加上k→j的距離小于i→j的距離dis[i][j]=dis[i][k]+dis[k][j];//更新最短路徑3.算法結(jié)束:dis[i][j] 即為 i→j 的最短路徑。
【模版】
int G[N][N]; int path[N][N];//path[i][j]=x表示i到j(luò)的路徑上除i外的第一個(gè)點(diǎn)是x void init(int n) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i == j)G[i][j] = 0;elseG[i][j] = INF;path[i][j] = j;}} } void floyd(int n) {for (int k = 1; k <= n; k++) { //枚舉中間點(diǎn)for (int i = 1; i <= n; i++) { //枚舉起點(diǎn)for (int j = 1; j <= n; j++) { //枚舉終點(diǎn)if (G[i][k] < INF && G[k][j] < INF) {if (G[i][j] > G[i][k] + G[k][j]) { //松弛操作G[i][j] = G[i][k] + G[k][j];path[i][j] = path[i][k]; //更新路徑} else if (G[i][j] == G[i][k] + G[k][j] && path[i][j] > path[i][k]) { //在最短路相同的情況下,更新字典序最小的路徑path[i][j] = path[i][k]; //最終path中存的是字典序最小的路徑}}}}} } int main() {int n, m;while (scanf("%d%d", &n, &m) != EOF) {init(n);for (int i = 1; i <= m; i++) {int x, y, dis;scanf("%d%d%d", &x, &y, &dis);//無向圖添邊一次,有向圖添邊兩次G[x][y] = dis;G[y][x] = dis;}floyd(n);for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++)printf("%d ", G[i][j]);printf("\n");}}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的图论 —— 最短路 —— Floyd 算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搬货物(51Nod-1596)
- 下一篇: 常用技巧 —— 打表规律