洛谷 P3371 模版 單源最短路徑(弱化版)

2021-10-01 05:53:41 字數 3031 閱讀 1004

給出乙個有向圖,請輸出從某一點出發到所有點的最短路徑長度。

題目鏈結【模板】單源最短路徑(弱化版) - 洛谷

第一行包含三個整數n、m、s,分別表示點的個數、有向邊的個數、出發點的編號。接下來m行每行包含三個整數fi、gi、wi,分別表示第i條有向邊的出發點、目標點和長度。

一行,包含n個用空格分隔的整數,其中第i個整數表示從點s出發到點i的最短路徑長度(若s=i則最短路徑長度為0,若從點s無法到達點i,則最短路徑長度為2147483647)

輸入4 6 1

1 2 2

2 3 2

2 4 1

1 3 5

3 4 3

1 4 4

輸出0 2 4 3

求單源最短路徑,最經典的演算法便是dijkstra,本質上是一種貪心的思想。

整體思路是:按路徑長度遞增的次序產生最短路徑的演算法。

利用乙個輔助陣列d,d[i]表示從起始點v0到點i的最短路徑,通過不斷更新這個陣列,最後就可以得到乙個點v0到各個點的最短路徑。

該陣列的初始狀態為:若從v0到vi有弧,則d[i]為邊長;否則將d[i]置為乙個極大值(比如說int的最大值2147483647)。

有了這個陣列後,我們可以從陣列中找到一條最短的弧,意義為:從v0到周圍中某乙個距離最近的點。

之後我們要找下一條長度次短的最短路徑,假設該次短路徑的終點是vk,則這條路徑為(v0,vk)或者(v0, vj, vk),vj即之前找到的最短路徑相連的那個點。

這裡有一條非常重要的定理:假設s為已求得最短路徑的終點的集合,則下一條最短路徑(設其終點為x)或者是弧(v0,x),或者是中間只經過s中的頂點而最後到達頂點x的路徑。(具體證明可以用反證法證明,在此不多贅述)

因此,下一條長度次短的最短路徑的長度必是

d[j] = min

其中di或者是弧(v0,vi)上的權值,或者是d[k](k屬於s)和弧(vk,vi)上的權值之和。

由此我們便可以得到dijkstra的演算法:

每次從輔助陣列d中找到最短路徑,並將對應的點標記為已經加入集合s,之後更新輔助陣列d的值,利用公式:

d[i] = min

(i為當前遍歷到的某個未被加入s的點,v為剛加入集合s的點)

重複上述操作,直至所有點都被加入s,此時的輔助陣列d就是從v0出發到各個頂點的最短路徑長度陣列了。

#include #define max 2147483647

#define max_point 10005

#define max_edge 500005

struct nodeedge[max_edge];

int pre[max_point];

int final[max_point],d[max_point];

int main()

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

index = pre[s];

while (edge[index].next != -1)

d[edge[index].v] = edge[index].w;

d[s] = 0;

final[s] = 1;

v = s;

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

final[v] = 1;

index = pre[v];

if (index == -1)

continue;

while (edge[index].next != -1)

}index = edge[index].next;

}if (final[edge[index].v] == 0)}}

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

printf("%d ", d[i]);

printf("%d", d[n]);

return 0;

}

資料結構:

struct nodeedge[max_edge];

int pre[max_point];

由於題目中n <= 10000,用鄰接矩陣存的話會爆記憶體,因此選擇用靜態鄰接表來存邊。其中pre儲存某個點的第一條邊在edge陣列中的位置,結構體node中,v代表有向邊指向的節點,w代表邊的權值,next代表同乙個節點出發的另一條邊的下標

int final[max_point],d[max_point];
final用來記錄節點是否被加入最短路徑(即是否在集合s中)

d即輔助陣列

初始化:

index = 1;

scanf("%d %d %d", &n, &m, &s);

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

pre[i] = -1; //初始化陣列pre

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

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

index = pre[s]; //獲得起始點出發的第一條邊在edges中的位置

while (edge[index].next != -1)

d[edge[index].v] = edge[index].w;

d[s] = 0; //起始點到起始點的距離當然設為0了

final[s] = 1; //起始點加入集合s

v = s;

dijkstra:

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

final[v] = 1; //將這條弧連線的點放入集合s

index = pre[v]; //獲得從這個點出發的第一條邊在edges中的下標

if (index == -1) //等於-1意味著沒有以這個節點為起點的弧

continue;

while (edge[index].next != -1)

}index = edge[index].next;

}if (final[edge[index].v] == 0)

}}

洛谷 P3371 模板 單源最短路徑

題目大意 在乙個有向圖中,有m條邊 1 m 500000 n個點 1 n 10000 求點s到1 n個點的最短路徑長度,無最短路就輸出maxlongint。spfa 佇列優化 dis i 表示點s到i的最短路徑,一開始dis陣列為maxlongint。1.用佇列優化,就可以省略列舉每個點的時間,由o...

洛谷P3371 模板 單源最短路徑

p3371 模板 單源最短路徑 看了b站上的spfa演算法講解,重新敲了一遍這個題,學習spfa演算法。題意 給出乙個有向圖,請輸出從某一點出發到所有點的最短路徑長度。spfa演算法是對bellman ford演算法的優化。後者複雜度為o nm 每一輪都對所有邊確定是否更新。前者將點加入佇列中,用b...

洛谷 P3371 模板 單源最短路徑

如題,給出乙個有向圖,請輸出從某一點出發到所有點的最短路徑長度。第一行包含三個整數n m s,分別表示點的個數 有向邊的個數 出發點的編號。接下來m行每行包含三個整數fi gi wi,分別表示第i條有向邊的出發點 目標點和長度。一行,包含n個用空格分隔的整數,其中第i個整數表示從點s出發到點i的最短...