最短路径问题

  今天我们来学学最短路径问题,最短路径问题分为两大类:单源最短路径问题和多源最短路径问题。通俗地讲,他们的区别就是单源最短路径问题确定了起点,多源最短路径问题没有确定起点。

 

单源最短路径问题:

  单源最短路径问题又分为无权图和有权图。

  对于无权图,我们只需一个简单的BFS就能完成找最短路径的工作。 

 1 #include <stdio.h>
 2 
 3 #define maxSize 100
 4 struct node {
 5     int to;
 6     int next;
 7 };
 8 struct node edge[maxSize];
 9 int head[maxSize], dis[maxSize], num;
10 
11 void add(int from, int to);
12 void shortestPath(int start, int n);
13 void myprintf(int start, int n);
14 
15 int main() {
16     int v, e;
17     //顶点与边的总数
18     scanf("%d %d", &v, &e);
19 
20     int i;
21     for (i = 1; i <= e; ++i) {
22         int from, to;
23         scanf("%d %d", &from, &to);
24         //以链式前向星的方式存储图
25         add(from, to);
26     }
27 
28     int x;
29     scanf("%d", &x);
30     //求以x为起点到各个顶点的最短路径
31     shortestPath(x, v);
32 
33     return 0;
34 }
35 
36 void shortestPath(int start, int n) {
37     int i;
38     for (i = 1; i <= n; ++i) {
39         dis[i] = -1;
40     }
41 
42     int queue[maxSize] = {0};
43     int queuehead = 0, queuetail = 1;
44     queue[queuehead] = start;
45     dis[start] = 0;
46 
47     while (queuehead < queuetail) {
48         int t = queue[queuehead];
49         ++queuehead;
50 
51         int index = head[t];
52         while (index) {
53             int point = edge[index].to;
54             if (dis[point] == -1) {
55                 dis[point] = dis[t] + 1;
56                 queue[queuetail] = point;
57                 ++queuetail;
58             }
59             index = edge[index].next;
60         }
61     }
62 
63     myprintf(start, n);
64 }
65 
66 void myprintf(int start, int n) {
67     int i;
68     for (i = 1; i <= n; ++i) {
69         printf("%d->%d: %d\n", start, i, dis[i]);
70     }
71 }
72 
73 void add(int from, int to) {
74     ++num;
75     edge[num].to = to;
76     edge[num].next = head[from];
77     head[from] = num;
78 }
单源最短路径之无权图

 

   对于有权图,一般优先采用Dijkstra算法。

  1 #include <stdio.h>
  2 
  3 #define inf 0x3f3f3f3f  //后面涉及inf的加法,为不超出范围,不采用0x7ffffff
  4 #define maxSize 520
  5 int dis[maxSize][maxSize];
  6 
  7 struct {
  8     int array[maxSize];
  9     int num;
 10 } heap;
 11 
 12 int v, e, start;
 13 
 14 void initialize(void);
 15 void Dijkstra(void);
 16 void buildHeap(void);
 17 int isEmpty(void);
 18 int pop(void);
 19 void up(int index);
 20 void down(int index);
 21 int smaller(int index1, int index2);
 22 void swap(int* p1, int* p2);
 23 
 24 int main() {
 25     scanf("%d %d %d", &v, &e, &start);
 26     initialize();
 27     int i;
 28     for (i = 0; i < e; ++i) {
 29         int from, to, d;
 30         scanf("%d %d %d", &from, &to, &d);
 31         dis[from][to] = d;
 32         dis[to][from] = d;
 33     }
 34 
 35     Dijkstra();
 36     
 37     for (i = 0; i < v; ++i) {
 38         printf("%d\n", dis[start][i]);
 39     }
 40 
 41     return 0;
 42 }
 43 
 44 void initialize(void) {
 45     int i, j;
 46     for (i = 0; i < v; ++i) {
 47         for (j = 0; j < v; ++j) {
 48             if (i == j) {
 49                 dis[i][j] = 0;
 50             }
 51             else {
 52                 dis[i][j] = inf;
 53             }
 54         }
 55     }
 56 }
 57 
 58 void Dijkstra(void) {
 59     buildHeap();
 60 
 61     while (!isEmpty()) {
 62         int t = pop();
 63 
 64         int i;
 65         for (i = 1; i <= heap.num; ++i) {
 66             if (dis[t][heap.array[i]] != inf) {
 67                 if (dis[start][heap.array[i]] > dis[start][t] + dis[t][heap.array[i]]) {
 68                     dis[start][heap.array[i]] = dis[start][t] + dis[t][heap.array[i]];
 69                     up(i);
 70                 }
 71             }
 72         }
 73     }
 74 }
 75 
 76 void buildHeap(void) {
 77     if (v == 1) {
 78         return ;
 79     }
 80 
 81     heap.num = v - 1;
 82     int i, t;
 83     for (i = 1, t = 0; i <= heap.num; ++i, ++t) {
 84         if (t == start) {
 85             ++t;
 86         }
 87         heap.array[i] = t;
 88     }
 89 
 90     for (i = heap.num/2; i > 0; --i) {
 91         up(i);
 92     }
 93 }
 94 
 95 int isEmpty(void) {
 96     if (heap.num) {
 97         return 0;
 98     }
 99     else {
100         return 1;
101     }
102 }
103 
104 int pop(void) {
105     int t = heap.array[1];
106     heap.array[1] = heap.array[heap.num];
107     --heap.num;
108     down(1);
109     return t;
110 }
111 
112 void up(int index) {
113     while (index/2 > 0) {
114         if (smaller(index, index/2)) {
115             swap(&heap.array[index], &heap.array[index/2]);
116             index /= 2;
117         }
118         else {
119             break;
120         }
121     }
122 }
123 
124 void down(int index) {
125     while (index * 2 <= heap.num) {
126         int minson = index * 2;
127         if (minson+1 <= heap.num && smaller(minson+1, minson)) {
128             ++minson;
129         }
130 
131         if (smaller(minson, index)) {
132             swap(&heap.array[minson], &heap.array[index]);
133             index = minson;
134         }
135         else {
136             break;
137         }
138     }
139 }
140 
141 int smaller(int index1, int index2) {
142     if (dis[start][heap.array[index1]] < dis[start][heap.array[index2]]) {
143         return 1;
144     }
145     else {
146         return 0;
147     }
148 }
149 
150 void swap(int* p1, int* p2) {
151     int t = (*p1);
152     (*p1) = (*p2);
153     (*p2) = t;
154 }
Dijkstra算法

  但需要注意的是,Dijkstra算法只能用于不含负权的图。若想处理含负权的图,我们需要SPFA算法。

 

多源最短路径问题:

  对于多源最短路径,我们很容易的想到,我们可以调用V(V为顶点个数)遍单源最短路径算法,这当然是一种解决办法,我们还有一种效率更高的算法:Floyd算法。但注意Floyd算法只能处理只含正权的图。

  Floyd算法的思想是这样的,首先我们将这个图存在一个dis数组中(以类似邻接矩阵的形式)。但这个数组要与邻接矩阵有所不同,对于dis[i][i],我们将其设置为0,对于一般的dis[i][j],若顶点i与顶点j之间无直接相连的边,我们将其设置为无穷大inf,若二者之间有直接相连的边,如果该图为有权图,则设置为权重,若为无权图,则设置为1。

  那么此时,dis[i][j]的含义就是顶点i与顶点j之间不经过第三个顶点时的最短路径。那么我们接下来考虑,要找到顶点i与j的真正最短路径,就是要对其余顶点进行遍历。对于其余每一个顶点,要么该顶点k处于i与j的最短路径上,则dis[i][k]+dis[k][j] < dis[i][j];要么不再i与j的最短路径上,则最短路径任为dis[i][j]。

 1 #include <stdio.h>
 2 
 3 //32位的int的inf本应是0x7ffffff,但在后面会涉及到一个数加上inf,会超出范围,故此处采用了一个较小的数
 4 #define inf 0x3f3f3f3f
 5 
 6 #define maxSize 105
 7 int dis[maxSize][maxSize]; //dis[i][j]i到j的最短路径,若不连通,则用inf表示
 8 
 9 void initialize(void);
10 void Floyd(int n);
11 
12 int main() {
13     initialize();   //初始化dis数组
14 
15     int v, e;
16     //v代表图中顶点总数,e代表边的总数
17     scanf("%d %d", &v, &e);
18     while (e--) {
19         //这是一个无向有权图,其他类型的图做法类似
20         int from, to, weight;
21         scanf("%d %d %d", &from, &to, &weight);
22         dis[from][to] = weight;
23         dis[to][from] = weight;
24     }
25 
26     Floyd(v);
27 
28     return 0;
29 }
30 
31 void initialize(void) {
32     int i, j;
33     for (i = 0; i < maxSize; ++i) {
34         for (j = 0; j < maxSize; ++j) {
35             if (i == j) {
36                 dis[i][j] = 0;
37             }
38             else {
39                 dis[i][j] = inf;
40             }
41         }
42     }
43 }
44 
45 void Floyd(int n) {
46     int i, j, k;
47     for (i = 1; i <= n; ++i) {
48         for (j = 1; j <= n; ++j) {
49             for (k = 1; k <= n; ++k) {
50                 if (dis[j][i] + dis[i][k] < dis[j][k]) {
51                     dis[j][k] = dis[j][i] + dis[i][k];
52                 }
53             }
54         }
55     }
56 }
Floyd算法

 

posted @ 2020-04-14 10:58  小茗从不写博客  阅读(716)  评论(0)    收藏  举报