相信大家都用过百度地图、高德地图之类的吧,凡是与地理信息系统相关的APP,必不可少的一个功能那就是“最优路径”的自动实现了。就是说用户只需要指定自己的起点和目的地,那么系统会自动帮用户挑选出一条最优路径出来,并将其显示在地图上。是不是很神奇呢?!
嗷,在进入正题之前先让yogurt来吐槽一下下!说到这个程序,那真是自己给自己挖的一个坑啊!自己一年前写的东西,本来想着来修改修改的,于是便把路网的权值初始化为MAX这一步给取消掉了,还以为自己之前是多此一举,后来调试了好久~~~~(>_<)~~~~看了半天终于搞明白了!这个MAX对构建最短路径表的时候判断是否更新尤为重要啊!想想心都碎了!(大家等会儿看程序的时候可以关注一下,如果把初始化为MAX去掉,会造成什么严重的后果)
所以下面我得讲得仔细一点,要不然一不小心你们也看不懂了(如果你说“yogurt,我轻轻松松就看懂了好吧!” 噫--那我收回前面说的话。毕竟我智商好像最近有点不够用了!急需你们借一点给我,哈哈哈!)
步入正题,我建议大家在看这篇程序之前可以先去了解一下Prim算法的内容,我的最短路径就是根据这个算法来实现的。如果你懒得去看了,那也没关系,因为我接下来会简单地把他算法的精髓挖出来给你们看一下。至于路网的存储构建方法,大家可以参考一下我的上一篇博客《构建带权路网》,在这里就不再重复介绍了。
==================================此处是精髓===================================233
假设有7个点,Prim算法就是很机智的把他们分为两部分,一部分是已经计算好最短路径的源(我们称为圈1),另一部分是未计算过的(我们称为圈2)。当然啦一开始的时候,所有的点都在圈2里面。每个点都有三个参数,分别是distance(该点到起点的最短距离),source_point(该点上一步经过的点),s(记录该点是否已经计算过即是否已经入圈1),以下简称d、p、s。
第一步:给定一个起点,我们修改好起点的参数之后就将起点从圈2放到圈1里面去。接下来更新圈2里面的每个点到圈1里面起点的距离(不与起点直接相连的点的d是MAX很大的一个值,意思就是不可直达)。更新好之后,找到distance最小的点把它放到圈1里面去,它也很荣幸升级成了圈1中的一员啦!
第二步:好,每放一个点进入圈1,我们都要修改这个点的参数(d、p、s),接着还需要更新圈2的点的d,为什么呢?因为对于与刚进入圈1 的点(比如p1点)直接相邻的点,说从起点经过p1之后再到这个点的距离比直接从起点过来的距离更近呢!所以这次的更新过程每个点都是需要判断是否需要更新的。更新完之后,也是找到distance最小的点把它放到圈1里面去,就这样圈1里面又添了一名家族成员。
然后重复第二步,一个一个的把圈2的点全部升级到圈1里面去,直到所有的点都在圈1里面了,那么从起点到每一个点的最短距离也就出来了。
===============================yogurt的小科普课堂结束===============================
让我今天纠结了这么久的当然不会就仅仅是一个Prim算法这样子啦!下面放大招了,代码呈上来!还添加了一个命令要求,比如你说“yogurt,我想从点0去点3,但是途中我一定要先去一趟点4呢!”嗷!你可真是神烦!”不过可难不倒聪明机智美丽善良的yogurt,哈哈哈哈哈哈哈~~


1 #include"stdafx.h" 2 #include"Graph.h" 3 #include"targetver.h" 4 #include<stdlib.h> 5 #define MAX_VERTEX_NUM 6 6 #define MAX 10000 7 8 typedef struct Vertex 9 { 10 int x, y; 11 }vertex; 12 13 typedef struct Graph 14 { 15 vertex ver[MAX_VERTEX_NUM]; 16 int weight[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; 17 int vexnum; 18 int arcnum; 19 }graph; 20 21 typedef struct Shortest 22 { 23 int distance, sourcepoint, s; 24 }shortest; //最短路径表 25 26 graph createroad(char * filename); 27 void printroad(graph G); 28 void findminroad(graph G, char x, shortest a[]); 29 30 void main() 31 { 32 graph G = createroad("information.txt"); //读入路网的信息 33 printroad(G); //输出路网结构 34 35 int x, y, w; //起点x,终点y,指定必过点w 36 int i, j; 37 38 printf("Please enter the start:"); 39 scanf("%d", &x); 40 41 shortest a[MAX_VERTEX_NUM]; 42 findminroad(G, x, a); //最短路径表a 43 44 printf("\nPlease enter the end:"); 45 scanf("%d", &y); 46 int save_y = y; 47 48 //-----------------------------------------------------------------------变色显示由x->y 49 do { 50 //x-->y的路径 51 setPenColor(RED); 52 moveTo(G.ver[a[y].sourcepoint].x, G.ver[a[y].sourcepoint].y); 53 lineTo(G.ver[y].x, G.ver[y].y); 54 y = a[y].sourcepoint; 55 } while (a[y].sourcepoint != MAX); 56 57 printf("\nPlease enter the point which must be passed:"); 58 scanf("%d", &w); 59 60 61 //画从w-->y的最小路径 62 shortest b[MAX_VERTEX_NUM]; 63 findminroad(G, w, b); //找到指定点w为起点的最短路径表b 64 65 //-----------------------------------------------------------------------变色显示由x->w->y 66 67 do{ 68 //x->w的路径 69 setPenColor(GREEN); 70 moveTo(G.ver[a[w].sourcepoint].x, G.ver[a[w].sourcepoint].y); 71 lineTo(G.ver[w].x, G.ver[w].y); 72 w = a[w].sourcepoint; 73 } while (a[w].sourcepoint != MAX); 74 75 y = save_y; 76 do{ 77 ////w->y的路径 78 setPenColor(BLUE); 79 moveTo(G.ver[b[y].sourcepoint].x, G.ver[b[y].sourcepoint].y); 80 lineTo(G.ver[y].x, G.ver[y].y); 81 y = b[y].sourcepoint; 82 } while (b[y].sourcepoint != MAX); 83 84 return; 85 } 86 87 graph createroad(char *filename) 88 { 89 graph g; 90 FILE* fp = fopen("information.txt", "r"); 91 if (fp) 92 { 93 fscanf(fp, "%d", &g.vexnum); //顶点数 94 for (int i = 0; i < g.vexnum; i++) //G的数组元素初始化 95 { 96 g.ver[i].x = 0; 97 g.ver[i].y = 0; 98 for (int j = 0; j < MAX_VERTEX_NUM; j++) 99 g.weight[i][j] = MAX; 100 } //初始化 101 fscanf(fp, "%d", &g.arcnum); //边数 102 for (int i = 0; i <g.vexnum; i++) //顶点数组 103 fscanf(fp, "%d,%d", &g.ver[i].x, &g.ver[i].y); 104 105 int v1, v2, w; 106 for (int i = 0; i < g.arcnum; i++) //构造邻接矩阵 107 { 108 fscanf(fp, "%d %d %d", &v1, &v2, &w); 109 g.weight[v1][v2] = w; 110 g.weight[v2][v1] = w; 111 } 112 }fclose(fp); 113 return g; 114 } 115 116 void printroad(graph G) 117 { 118 setOrig(0, 0); 119 setPenColor(WHITE); 120 for (int i = 0; i < G.vexnum; ++i) 121 { 122 drawRectangle(G.ver[i].x, G.ver[i].y); 123 124 char label[10]; 125 sprintf(label, "%d", i); 126 127 drawText(G.ver[i].x, G.ver[i].y, label); 128 129 for (int j = 0; j <G.vexnum; ++j) 130 { 131 if (G.weight[i][j]!=MAX) 132 { 133 moveTo(G.ver[i].x, G.ver[i].y); 134 lineTo(G.ver[j].x, G.ver[j].y); 135 } 136 } 137 } 138 } 139 140 void findminroad(graph G, char x, shortest a[]) 141 { 142 //初始化a[] 143 for (int i = 0; i < G.vexnum; i++) 144 a[i].s = 0; 145 146 int m = x, min = MAX, q = G.vexnum; 147 a[m].s = 1; //标识已入源 148 a[m].sourcepoint = MAX; 149 150 int j; 151 int t; 152 153 for (int i = 0; i < G.vexnum; i++) 154 { 155 if (a[i].s == 0) //在未入源中进行 156 { 157 a[i].distance = G.weight[m][i]; 158 a[i].sourcepoint = m; 159 if (a[i].distance !=MAX && a[i].distance < min) //标识相邻且最小距离点 160 { 161 j = i; //j记录最小距离点 162 min = a[i].distance; 163 } 164 } 165 }//第二个点入源,不需要更新distance 166 167 do{ 168 m = j; //j入源 169 a[m].s = 1; //标识已入源 170 171 min = MAX; //每次都必须为min赋值为MAX 172 for (int i = 0; i < G.vexnum; i++) 173 { 174 if (a[i].s == 0 && G.weight[m][i]>0) //在未入源且与新加入点相邻的点中进行 175 { 176 t = a[m].distance + G.weight[m][i]; //t判断是否需要更新 177 if (t < a[i].distance) //需要更新 178 { 179 a[i].distance = t; 180 a[i].sourcepoint = m; 181 } 182 if (a[i].distance < min) //标识最小距离点 183 { 184 j = i; 185 min = a[i].distance; //min更新 186 } 187 } 188 }//distance更新完毕 189 q--; 190 } while (q>1); //执行G.vexnum-1次 191 return; 192 }
接下来就是yogurt给你们见证奇迹的时刻啦~~
(这里解释一下:路网原本都是用白色画的哈,红色的是直接从点0到达点3的路径;蓝色的路径是从点0出发,经过点4之后再到达点3的最优路径。
哇塞,是不是很神奇,原来那些地图APP上的最优路径就是这样实现的!不过,也许yogurt的代码有的地方写得太啰嗦了点,欢迎大家批评指正哈!