最短路径问题
今天我们来学学最短路径问题,最短路径问题分为两大类:单源最短路径问题和多源最短路径问题。通俗地讲,他们的区别就是单源最短路径问题确定了起点,多源最短路径问题没有确定起点。
单源最短路径问题:
单源最短路径问题又分为无权图和有权图。
对于无权图,我们只需一个简单的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算法只能用于不含负权的图。若想处理含负权的图,我们需要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 }

浙公网安备 33010602011771号