給出乙個有向圖,請輸出從某一點出發到所有點的最短路徑長度。
題目鏈結【模板】單源最短路徑(弱化版) - 洛谷
第一行包含三個整數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的最短...