迪杰斯特拉算法
迪杰斯特拉(Dijkstra)算法是最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心一层一层的向外走(广度优先搜索),直到找到终点
先看具体的例子来体会下它的算法思想:
- dist[]:起点到未被并入的顶点的最短距离
【类比】Prim算法中的lowCost[]:当前生成树到为并入的顶点的最短距离 - path[]:起点到该顶点的最短路径
- S[]:已并入的顶点
步骤
【第一步】
【第二步】
【步骤三】
【步骤四】
【步骤五】
【步骤六】
【算法结束】
- dist[v]:起点0到v的最短路径长度
- path[v]:起点0到v的最短路径
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <stack> 7 8 #define INF 0x3f3f3f3f 9 using namespace std; 10 const int MAXN = 10005; 11 12 int graph[MAXN][MAXN]; 13 int dist[MAXN]; 14 bool vis[MAXN]; 15 int pre[MAXN]; 16 int n,m; 17 18 stack<int> path; 19 20 void dijistra(int x){ 21 int pos = x; 22 memset(pre,-1, sizeof(pre)); 23 for(int i = 1; i <= n; i ++){ 24 dist[i] = graph[pos][i]; 25 } 26 vis[pos] = true; 27 for(int i = 1; i <= n; i ++){ 28 int mins = INF; 29 for(int j = 1; j <= n; j ++){ 30 if(!vis[j] && dist[j] < mins){ 31 pos = j; 32 mins = dist[j]; 33 } 34 } 35 vis[pos] = true; 36 for(int j = 1; j <= n; j ++){ 37 if(!vis[j] && dist[j] > dist[pos] + graph[pos][j]){ 38 dist[j] = dist[pos] + graph[pos][j]; 39 pre[j] = pos; 40 } 41 } 42 } 43 } 44 45 int main() 46 { 47 freopen("../in.txt","r",stdin); 48 int T; 49 scanf("%d",&T); 50 while (T--) 51 { 52 scanf("%d%d",&n,&m); 53 for (int i=1;i<=n;i++) 54 { 55 for (int j=1;j<=n;j++) 56 { 57 if (i == j) 58 graph[i][j] = 0; 59 else 60 graph[i][j] = INF; 61 } 62 } 63 for (int i=1;i<=m;i++) 64 { 65 int a,b,c; 66 scanf("%d%d%d",&a,&b,&c); 67 graph[a][b] = graph[b][a] = c; 68 } 69 70 int start,end; 71 scanf("%d%d",&start,&end); 72 memset(vis, false, sizeof(vis)); 73 dijistra(start); 74 int temp = end; 75 while (pre[temp]!=-1) 76 { 77 path.push(temp); 78 temp = pre[temp]; 79 } 80 path.push(temp); 81 cout << "path: " << start << " "; 82 while (!path.empty()) 83 { 84 printf("%d ",path.top()); 85 path.pop(); 86 } 87 cout << endl; 88 cout << "distance:" << endl; 89 printf("%d\n",dist[end]); 90 } 91 return 0; 92 }
上面的是数组的写法,但是有的时候题目的数据量太大往往会导致数组的写法不能够使用。
因为不能够开这么大的数组,嘤嘤嘤
所以现在重点来了,我们使用邻接矩阵的写法并且采取优先队列的方式对我们Dj算法进行优化:
模版题: poj1511:http://poj.org/problem?id=1511
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <string> 7 #include <string.h> 8 #include <math.h> 9 #include <vector> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 14 #define INF 0x3f3f3f3f 15 #define LL long long 16 #define MAXN 1000001 17 using namespace std; 18 19 typedef pair<int,int> P; //first 是最短距离,second 是编号 20 struct edge{ // 边 21 int f; // 起点 22 int t; // 终点 23 int c; // 花费 24 edge(){ 25 f = 0; 26 t = 0; 27 c = 0; 28 } 29 edge(int ff,int tt,int cc){ 30 f = ff; 31 t = tt; 32 c = cc; 33 } 34 }; 35 int n,m; 36 int dist[MAXN]; // 记录距离 37 vector<edge> graph[MAXN]; // 领接矩阵 38 edge edges[MAXN]; // 存边的数组 39 bool vis[MAXN]; 40 41 void dijkstra(int x) 42 { 43 priority_queue<P,vector<P>,greater<P> > Q; 44 for (int i=1;i<=n;i++) 45 dist[i] = INF; 46 dist[x] = 0; 47 memset(vis,false, sizeof(vis)); 48 Q.push(P(0,x)); 49 while (!Q.empty()) 50 { 51 int v = Q.top().second; 52 Q.pop(); 53 if (vis[v]) //如果更新过 54 continue; 55 vis[v] = true; 56 // 遍历当前点相邻的所有点 57 for (int i=0;i<graph[v].size();i++) 58 { 59 edge e = graph[v][i]; 60 if (dist[e.t]>dist[v]+e.c) //找最短路 61 { 62 dist[e.t] = dist[v] + e.c; 63 Q.push(P(dist[e.t],e.t)); 64 } 65 } 66 } 67 } 68 69 void init() 70 { 71 for (int i=0;i<=n;i++) 72 graph[i].clear(); 73 } 74 75 76 int main() 77 { 78 //freopen("../in.txt","r",stdin); 79 int T; 80 scanf("%d",&T); 81 while (T--) 82 { 83 scanf("%d%d",&n,&m); 84 init(); 85 for (int i=1;i<=m;i++) 86 { 87 int a,b,c; 88 scanf("%d %d %d",&a,&b,&c); 89 edges[i] = edge(a,b,c); 90 graph[a].push_back(edge(a,b,c)); // a 为顶点 91 //graph[b].push_back(edge(b,a,c)); 无向图 92 } 93 dijkstra(1); 94 long long int sum = 0; 95 for (int i=1;i<=n;i++) 96 sum += dist[i]; 97 init(); 98 for (int i=1;i<=m;i++) 99 { 100 edge tmp = edges[i]; 101 graph[tmp.t].push_back(edge(tmp.t,tmp.f,tmp.c)); 102 } 103 dijkstra(1); 104 for (int i=1;i<=n;i++) 105 sum += dist[i]; 106 printf("%lld\n",sum); 107 } 108 return 0; 109 }
前向星加链式存储加优先队列优化
1 #include <stdio.h> 2 #include <iostream> 3 #include <algorithm> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <queue> 7 8 #define INF 0x3f3f3f3f 9 #define pii pair<int,int> 10 using namespace std; 11 const int MAXN = 2e5+7; 12 13 int head[MAXN]; 14 int dist[MAXN]; 15 bool vis[MAXN]; 16 int cnt; 17 18 struct Edge{ 19 int to,val,next; 20 }edge[MAXN]; 21 22 void init(){ 23 cnt = 0; 24 memset(head,-1, sizeof(head)); 25 memset(vis,false, sizeof(vis)); 26 memset(dist,INF, sizeof(dist)); 27 } 28 29 void add(int u,int v,int w){ 30 edge[cnt].to = v; 31 edge[cnt].val = w; 32 edge[cnt].next = head[u]; 33 head[u] = cnt++; 34 } 35 36 void dijstra(int s) 37 { 38 priority_queue<pii,vector<pii>,greater<pii> > q; 39 dist[s] = 0; 40 q.push({dist[s],s}); 41 while (!q.empty()) 42 { 43 int now = q.top().second; 44 q.pop(); 45 if (vis[now]) 46 continue; 47 vis[now] = true; 48 for (int i=head[now];i!=-1;i=edge[i].next) 49 { 50 int v = edge[i].to; 51 if (dist[v]>dist[now]+edge[i].val) 52 { 53 dist[v] = dist[now] + edge[i].val; 54 q.push({dist[v],v}); 55 } 56 } 57 } 58 } 59 60 int main() 61 { 62 int n,m,s; 63 while (~scanf("%d%d",&n,&m)) 64 { 65 init(); 66 while (m--) 67 { 68 int a,b,c; 69 scanf("%d%d%d",&a,&b,&c); 70 add(a,b,c); 71 } 72 dijstra(1); 73 printf("%d\n",dist[n]); 74 } 75 return 0; 76 }