本文主要讨论了最短路径的三个算法:Floyd,Dijkstra以及Bellman_Ford的实现方法以及他们之间的区别与联系
1 /** 2 Floyd算法流程: 3 若edge[i][j]表示从结点i到结点j,中间只能经过编号小于k的点时的最短路径长度, 4 我们可以由这些值确定当中间允许经过编号小于等于k的结点时,它们之间的最短路径长 5 度。同样,与原情况相比,新情况中允许 出现在中间路径的结点新增了编号为 k 的结 6 点,同理我们确定 edge[i][k] + edge[k][j]的值与edge[i][j]的值,若前者较小则该值代 7 表了新情况中从结点i到结 点j的最短路径长度;否则,新情况中该路径长度依旧保持不变。 8 9 Floyd算法注意事项: 10 (1)时间复杂度,O(N^3),所以在节点个数大于200时,考虑超时问题; 11 (2)用一个二维矩阵表示图形,如果原图并非由邻接矩阵给出时,设法转换,特别地, 12 当两个节点之间有多余一条边时,我们选择长度最小的边权值存入邻接矩阵; 13 (3)放算法完成后,所有节点之间的最短路径都被确定,适合——全源最短路径问题。 14 15 样例输入: 16 2 1 17 1 2 3 18 3 3 19 1 2 5 20 2 3 5 21 3 1 2 22 0 0 23 样例输出: 24 3 25 2 26 */ 27 28 #include<cstdio> 29 using namespace std; 30 31 int ans[101][101];//二维数组,其初始值即为该图的邻接矩阵 32 33 int main() 34 { 35 int n, m; 36 while(scanf_s("%d%d", &n, &m) != EOF)//输入节点个数以及边个数 37 { 38 if(n ==0 && m == 0) 39 break; 40 41 for(int i = 1 ; i < n ; i ++) 42 { 43 for(int j = 0 ; j <=n ; j ++) 44 { 45 ans[i][j] = -1;//对邻接矩阵初始化,-1表示无穷大 46 } 47 ans[i][i] = 0;//自己到自己的路径长度设置为0 48 } 49 50 //输入边信息 51 while(m --) 52 { 53 int a, b, c; 54 scanf_s("%d%d%d", &a, &b, &c); 55 ans[a][b] = ans[b][a] = c;//无向图,赋值操作进行两次 56 } 57 58 for(int k = 1 ; k <= n ; k ++)//k从1到N循环,一次代表允许经过的中间节点编号小于等于k 59 { 60 for(int i = 1 ; i <= n ; i ++) 61 { 62 for(int j = 1 ; j <= n ; j ++)//遍历所有的ans[i][j],判断气质保持原值,还是将要被更新 63 { 64 if(ans[i][k] == -1 || ans[k][j] == -1)//若两值中有一个值为无穷,则ans[i][j]不能经过节点k而被更新,跳过循环,保持原值 65 continue; 66 67 if(ans[i][j] == -1 || ans[i][k] + ans[j][k] < ans[i][j])//当由于经过k可以获得更短的最短路径时,更新该值 68 ans[i][j] = ans[i][k] + ans[j][k]; 69 } 70 } 71 } 72 73 printf_s("%d ", ans[1][n]);//循环输出最后答案 74 } 75 76 return 0; 77 } 78 79 80 /** 81 Dijkstra算法流程: 82 (1)初始化,集合K中加入节点1,节点1到节点1的最短路径为0,到其他节点为无穷(或不确定); 83 (2)遍历集合K中与集合K中节点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算由节点1 84 出发按照已经得到的最短路到达U,再由U经过该边到达V时的路径长度。比较所有与集合K中节点直接相邻的 85 非集合K节点该路径长度,其中路径长度最小的节点被确定为下一个最短路径确定的节点,其最短路径长度即为 86 这个路径长度,最后将该节点加入集合K; 87 (3)若集合K中已经包含了所有的点,算法结束;否则重复步骤2; 88 89 Dijkstra算法注意事项 90 (1)单源最短路径问题,即只能计算出某个特定节点到其他所有节点的最短路径长度; 91 (2)支持邻接链表,在节点比较多时,可以使用这种方法 92 93 样例输入: 94 2 1 95 1 2 3 96 3 3 97 1 2 5 98 2 3 5 99 3 1 2 100 0 0 101 样例输出: 102 3 103 2 104 */ 105 106 #include<cstdio> 107 #include<vector> 108 using namespace std; 109 110 struct E//邻接表中链表原色结构体(由一个节点所持有) 111 { 112 int next;//代表直接相邻的点 113 int c;//代表该边的权重 114 }; 115 116 vector<E> edge[101];//邻接表 117 bool mark[101];//标记,当mark[j]为true时,表示节点j的最短路径长度已经得到,该节点已经加入集合K 118 int Dis[101];//距离向量,当mark[k]为true时,表示已经得到最短路径; 119 //否则表示所有从节点1出发,经过已知的最短路径达到集合K中的某点,在经过一条边到达节点i的路径中最短距离 120 121 int main() 122 { 123 int n , m; 124 125 while(scanf_s("%d%d", &n, &m) != EOF)//节点以及边个数 126 { 127 if(n == 0 && m ==0) 128 break; 129 130 for(int i = 1 ; i <= n ; i ++) 131 edge[i].clear();//清空上一次数据 132 133 while(m --) 134 { 135 int a, b, c;//节点a,节点b,权重c 136 scanf_s("%d%d%d", &a, &b, &c); 137 138 E tmp; 139 tmp.c = c; 140 tmp.next = b; 141 edge[a].push_back(tmp); 142 tmp.next = a; 143 edge[b].push_back(tmp);//将邻接信息加入邻接表,由于是无向图,从而每条边信息都要添加到其链各个两个顶点的两条单链表中 144 } 145 146 for(int i = 1 ; i <= n ; i ++)//初始化 147 { 148 Dis[i] = -1;//所有距离为-1,即不可达 149 mark[i] = false;//所有节点不属于集合K 150 } 151 152 Dis[1] = 0;//得到最近的点为节点1,长度为0 153 mark[1] = true;//将节点1加入集合K 154 int newP = 1;//集合K中新加入的点为节点1 155 for(int i = 1 ; i < n ; i ++)//循环n - 1次,按照最短路径递增的顺序确定其他n - 1个点的最短路径长度 156 { 157 for(int j = 0 ; j < edge[newP].size() ; j++)//遍历与该新加入集合K中的节点直接相邻的边 158 { 159 int t = edge[newP][j].next;//该边的下一个节点 160 int c = edge[newP][j].c;//该边的权重 161 162 if(mark[t] == true)//若另一个节点也属于集合K,则跳过 163 continue ; 164 165 if(Dis[t] == -1 || Dis[t] > Dis[newP] + c)//若该节点尚不可达或者该节点新加入的节点经过一条边到达时比以往距离更短 166 Dis[t] = Dis[newP] + c;//更新距离信息 167 } 168 169 int min = 123123123;//最小值初始化为一个大整数,为找最小值做准备 170 for(int j = 1 ; j <= n ; j ++)//遍历所有节点 171 { 172 if(mark[j] == true)//若其属于集合K,则跳过 173 continue ; 174 if(Dis[j] == -1)//若该节点仍不可达,则跳过 175 continue ; 176 if(Dis[j] < min)//若该节点经由节点1到集合K的某点在经过一条边到达时距离小于当前最小值 177 { 178 min = Dis[j];//更新其为最小值 179 newP = j;//新加入的点,暂定为该节点 180 } 181 } 182 mark[newP] = true;//将新加入的点加入集合K,Dis[newP]虽然数值不变,但意义发生变化,由所有经过集合K中的节点在经过一条边到达时的距离中的最小值变为从节点1到节点newP的最短距离 183 } 184 185 printf_s("%d ", Dis[n]);//输出 186 } 187 188 return 0; 189 } 190 191 /** 192 Dijkstra算法流程: 193 (1)初始化,集合K中加入节点1,节点1到节点1的最短路径为0,到其他节点为无穷(或不确定); 194 (2)遍历集合K中与集合K中节点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算由节点1 195 出发按照已经得到的最短路到达U,再由U经过该边到达V时的路径长度。比较所有与集合K中节点直接相邻的 196 非集合K节点该路径长度,其中路径长度最小的节点被确定为下一个最短路径确定的节点,其最短路径长度即为 197 这个路径长度,最后将该节点加入集合K; 198 (3)若集合K中已经包含了所有的点,算法结束;否则重复步骤2; 199 200 Dijkstra算法注意事项 201 (1)单源最短路径问题,即只能计算出某个特定节点到其他所有节点的最短路径长度; 202 (2)支持邻接链表,在节点比较多时,可以使用这种方法 203 204 样例输入: 205 4 6 1 206 1 2 20 207 1 3 5 208 4 1 -200 209 2 4 4 210 4 2 4 211 3 4 2 212 4 6 1 213 1 2 2 214 1 3 5 215 4 1 10 216 2 4 4 217 4 2 4 218 3 4 2 219 220 样例输出: 221 have negative circles 222 0 223 2 224 5 225 6 226 */ 227 228 229 #include <iostream> 230 using namespace std; 231 const int maxnum = 100; 232 const int maxint = 99999; 233 234 // 边, 235 typedef struct Edge{ 236 int u, v; // 起点,重点 237 int weight; // 边的权值 238 }Edge; 239 240 Edge edge[maxnum]; // 保存边的值 241 int dist[maxnum]; // 结点到源点最小距离 242 243 int nodenum, edgenum, source; // 结点数,边数,源点 244 245 // 初始化图 246 void init() 247 { 248 // 输入结点数,边数,源点 249 cin >> nodenum >> edgenum >> source; 250 for(int i = 1 ; i <= nodenum ; ++i) 251 dist[i] = maxint; 252 dist[source] = 0; 253 for(int i = 1 ; i <= edgenum ; ++i) 254 { 255 cin >> edge[i].u >> edge[i].v >> edge[i].weight;//输入边以及权重 256 if(edge[i].u == source) //注意这里设置初始情况 257 dist[edge[i].v] = edge[i].weight; 258 } 259 } 260 261 // 松弛计算 262 void relax(int u, int v, int weight) 263 { 264 if(dist[v] > dist[u] + weight) 265 dist[v] = dist[u] + weight; 266 } 267 268 bool Bellman_Ford() 269 { 270 for(int i = 1; i <= nodenum - 1; ++i) 271 for(int j = 1 ; j <= edgenum ; ++j) 272 relax(edge[j].u, edge[j].v, edge[j].weight); 273 bool flag = 1; 274 // 判断是否有负环路 275 for(int i = 1; i <= edgenum; ++i) 276 if(dist[edge[i].v] > dist[edge[i].u] + edge[i].weight) 277 { 278 flag = 0; 279 break; 280 } 281 return flag; 282 } 283 284 int main() 285 { 286 init(); 287 288 if(Bellman_Ford()) 289 { 290 for(int i = 1 ;i <= nodenum; i++) 291 cout << dist[i] << endl; 292 } 293 else 294 printf_s("have negative circles "); 295 return 0; 296 }