#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 500; int n, m, g[N][N], d[N]; bool st[N]; int dijkstra() { memset(d, 0x3f, sizeof d);//两点之间和1到n-1个点之间的初始值都为+oo d[1] = 0; for(int i = 0;i<n;i++) { int t = -1; for(int j = 1;j<=n;j++) if(!st[j] && (t == -1 || d[t] > d[j])) t = j; st[t] = true; for(int j = 1; j <= n;j++) d[j] = min(d[j], d[t] + g[t][j]); } if(d[n] == 0x3f3f3f3f) return -1; return d[n]; } int main() { memset(g, 0x3f, sizeof g); cin>>n>>m; for(int i = 0; i < n; i++) { int a, b, c; cin>>a>>b>>c; g[a][b] = min(g[a][b], c); } cout<<dijkstra()<<endl; }
起始点为1,1到自己的距离d[1] = 0; 走过的点就打好标记不再考虑了,st[t] = true;
任意两点之间和1到n-1个点之间的初始值都为无穷大+oo,g[]和d[]
每次向后拓展的时候都选择当前最短的边:d[t] > d[j] t = j;
若后面的点更新有更小的距离,那么就进行更新:d[j] = min(d[j], d[t] + g[t][j]);
注意Dijkstra如果有n个点的话,那么就要循环n次,也就是说,n个点没有负权的图(可以有自环和重边),通过跑n次Dijkstra一定可以跑出1到其他n-1个点的最短距离。
为什么如果有负权边不能用Dijkstra呢?因为如果有负数环的话,每跑一次距离就变小一次。。。
1≤n≤5001≤n≤500,
1≤m≤1051≤m≤105,
图中涉及边长均不超过10000。
此题点数较少,变数较多称为稠密图。上面的邻接矩阵版本是朴素的裸Dijkstra算法。
但是如果数据范围是:
数据范围
1≤n,m≤1.5×1051≤n,m≤1.5×105,
图中涉及边长均不小于0,且不超过10000。
那么就是稀疏图,如果还是用上面的邻接矩阵的话,10^5 * 10 ^ 5会爆掉。
那么就要用堆优化版本的Dijkstra算法。
#include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 150000; typedef pair<int,int> PII; int n, m, h[N], e[N], ne[N], w[N], d[N], idx; bool st[N]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++; } int dijkstra() { memset(d, 0x3f, sizeof d); d[1] = 0; priority_queue<PII, vector<PII>, greater<PII> >heap; heap.push({0,1}); while(heap.size()) { auto 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(d[j] > distance + w[i]){ d[j] = distance + w[i]; heap.push({d[j],j}); } } } if(d[n] == 0x3f3f3f3f) return -1; return d[n]; } int main() { memset(h, -1, sizeof h); cin>>n>>m; for(int i = 0; i < m; i++) { int a, b, c; cin>>a>>b>>c; add(a, b, c); } cout<<dijkstra()<<endl; }
Dijkstra()为什么不能有负权边?
因为每次都是选择一个点,然后选择距离它最小的点来更新到下一个点的,而如果在一个较大距离点的后面有一个负权值,而通过这个较大边再由这个负权边到达原来已经通过最短边得到的最短距离还要更短的话,
那么Dijkstra得到的最短距离就是错的,所以不能处理有负权边的情况。而如果边的权值全都是正的话,那么通过一个较大距离的边出发再回到由同个点的话,肯定会比一开始最短距离更新到的边要大,两边之和大于第三边,但是有负数的话就除外了。
spfa算法是由bellman_ford算法改进而来,但是跟Dijkstra()算法长得很像,能用Dijkstra算法的都能用spfa来做,有负权边的也只能用sfpa来做。