100天程式碼提升計劃-第14天
阿新 • • 發佈:2022-03-17
單源最短路
正權邊
Dijkstra演算法 O(n^2)
每次通過已知最短距離來更新到其他點的最短路
注意出現重邊要進行比較
#include<iostream> #include<algorithm> using namespace std; const int N = 1e5+10; int g[N][N];//鄰接矩陣 int dist[N];//源點到其他點的距離 bool st[N];//判斷是否被認定為已經是最短 // 求1號點到n號點的最短路,如果不存在則返回-1 int dijkstra() { memset(dist, 0x3f, sizeof dist); dist[1] = 0; for(int i = 0; i < n - 1; i ++ ) { int t = -1;// 在還未確定最短路的點中,尋找距離最小的點 for(int j = 1; j <= n; j ++ ) if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j; // 用t更新其他點的距離 for(int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], dist[t] + g[t][j]); st[t] = true; } if(dist[n] == 0x3f3f3f3f) return -1; return dist[n]; }
堆優化的Dijkstra演算法 O(mlogn)
如果是稀疏圖,點數量多,遍歷的時候就會超時,所以用鄰接表儲存,每次取最短距離的步驟可以用最小堆優化
#include <iostream> #include <queue> using namespace std; typedef pair<int, int> PII; const int N = 1e5 + 10; int n; int h[N], w[N], ne[N], e[N], idx; int dist[N]; int st[N]; int Dijkstra() { memset(h, -1, sizeof h); dist[1] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, 1}); while (heap.size()) { PII t = heap.top(); heap.pop(); int ver = t.second, distance = t.first; if (st[ver]) continue; st[ver] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > distance + w[i]) { dist[j] = distance + w[i]; heap.push({dist[j], j}); } } } if (dist[n] == 0x3f3f3f3f) return -1; return dist[n]; } int main() { return 0; }
負權邊
Bellman-ford演算法 O(nm)
Dijkstra演算法無法處理有負權邊的圖
一層一層的去更新
有限步數的條件下只能用Bellman-ford演算法
int n, m; // n表示點數,m表示邊數 int dist[N]; // dist[x]儲存1到x的最短路距離 struct Edge // 邊,a表示出點,b表示入點,w表示邊的權重 { int a, b, w; }edges[M]; // 求1到n的最短路距離,如果無法從1走到n,則返回-1。 int bellman_ford() { memset(dist, 0x3f, sizeof dist); dist[1] = 0; // 如果第n次迭代仍然會鬆弛三角不等式,就說明存在一條長度是n+1的最短路徑,由抽屜原理,路徑中至少存在兩個相同的點,說明圖中存在負權迴路。 for (int i = 0; i < n; i ++ ) { for (int j = 0; j < m; j ++ ) { int a = edges[j].a, b = edges[j].b, w = edges[j].w; if (dist[b] > dist[a] + w)//從源點開始更新,每次更新一層(這是因為正無窮更新之後還是正無窮,所以可以一層一層更新) dist[b] = dist[a] + w; } } if (dist[n] > 0x3f3f3f3f / 2) return -1; return dist[n]; }
spfa演算法 O(m)
佇列優化的Bellman_ford演算法
可以用來判斷是否有負權迴路,需要時只需要加一個cnt陣列來記錄點數,如果點數>n就說明存在負權環
int n; // 總點數
int h[N], w[N], e[N], ne[N], idx; // 鄰接表儲存所有邊
int dist[N]; // 儲存每個點到1號點的最短距離
bool st[N]; // 儲存每個點是否在佇列中
// 求1號點到n號點的最短路距離,如果從1號點無法走到n號點則返回-1
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
auto t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j]) // 如果佇列中已存在j,則不需要將j重複插入
{
q.push(j);
st[j] = true;
}
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
多源匯最短路
Floyd演算法
初始化:
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
// 演算法結束後,d[a][b]表示a到b的最短距離
void floyd()
{
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}