第六章 最短路径——有向图(Floyd-Warshall、Dijkstra、Bellman-Ford)
一、Floyd-Warshall——加入点(多源最短路径,核心算法只有五行)
城市之间的最短路径
输入:
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
输出:
0 2 5 4
9 0 3 4
6 8 0 1
5 7 10 0
1 #include <stdio.h> 2 #define Max 99999 3 int main() 4 { 5 int map[10][10]; 6 int n, m, x, y, t; 7 int i, j, k; 8 scanf("%d%d",&n,&m); 9 for (i = 1;i <= n;i++) 10 { 11 for (j = 1;j <= n;j++) 12 { 13 if (i == j) 14 map[i][j] = 0; 15 else 16 map[i][j] = Max; 17 } 18 } 19 for (i = 1;i <= m;i++) 20 { 21 scanf("%d%d%d",&x,&y,&t); 22 map[x][y] = t; 23 } 24 for (k = 1;k <= n;k++) 25 { 26 for (i = 1;i <= n;i++) 27 { 28 for (j = 1;j <= n;j++) 29 { 30 if (map[i][k] < Max&&map[k][j]<Max&&map[i][j]>map[i][k] + map[k][j]) 31 { 32 map[i][j] = map[i][k] + map[k][j]; 33 } 34 } 35 } 36 } 37 for (i = 1;i <= n;i++) 38 { 39 for (j = 1;j <= n;j++) 40 printf("%d ",map[i][j]); 41 printf("\n"); 42 } 43 return 0; 44 }
二、Dijkstra——加入边(单源最短路径)
输入:第一行n(顶点个数,编号从1开始),m(边的条数);接下来m行,每行3个数x,y,z,表示顶点x到y边的权值为z
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
输出:1号顶点到其余各个顶点(1,2,3,4,5,6)的最短距离
0 1 8 4 13 17
使用二位数组存储边
1 #include <stdio.h> 2 #define Max 99999 3 int main() 4 { 5 int map[10][10],dis[10],book[10]; 6 int n, m, x, y, t; 7 int i, j, k; 8 int min,mini; 9 scanf("%d%d",&n,&m); 10 for (i = 1;i <= n;i++) 11 { 12 for (j = 1;j <= n;j++) 13 { 14 if (i == j) 15 map[i][j] = 0; 16 else 17 map[i][j] = Max; 18 } 19 } 20 for (i = 1;i <= m;i++) 21 { 22 scanf("%d%d%d",&x,&y,&t); 23 map[x][y] = t; 24 } 25 for (i = 1;i <= n;i++) 26 dis[i] = map[1][i]; 27 for (i = 1;i <= n;i++) 28 book[i] = 0; 29 book[1] = 1; 30 for (i = 1;i <n;i++) 31 { 32 min = Max; 33 for (j = 1;j <= n;j++) 34 { 35 if (book[j] == 0 && min > dis[j]) 36 { 37 min = dis[j]; 38 mini = j; 39 } 40 } 41 book[mini] = 1; 42 for (j = 1;j <= n;j++) 43 { 44 if (map[mini][j] < Max) 45 { 46 if (dis[j] > dis[mini] + map[mini][j]) 47 { 48 dis[j] = dis[mini] + map[mini][j]; 49 } 50 } 51 } 52 } 53 for (i = 1;i <= n;i++) 54 { 55 printf("%d ",dis[i]); 56 } 57 printf("\n"); 58 return 0; 59 }
使用邻接表存储边,针对稀疏图,此时的存储方式,节约空间
1 #include <stdio.h> 2 #define Max 1000 3 int main() 4 { 5 int n, m, x[10], y[10], z[10]; 6 int book[10] = { 0 }, dis[10],first[10],next[10]; 7 int i, j, min, minx,sum; 8 scanf("%d%d",&n,&m); 9 for (i = 1;i <= n;i++) 10 first[i] = -1; 11 for (i = 1;i <= m;i++) 12 { 13 scanf("%d%d%d", x+i,y+i,z+i); 14 next[i] = first[x[i]];//first为表头,next为链表 15 first[x[i]] = i;//first和next中全部记录的是输入带方向边的编号 16 } 17 minx = 1; 18 book[minx] = 1; 19 for (i = 1;i <= n;i++) 20 dis[i] = Max; 21 dis[minx] = 0; 22 sum = 0; 23 while (sum <= n) 24 { 25 min = Max; 26 for (j = 1;j <= n;j++) 27 { 28 if (book[j] == 0 && dis[j] < min) 29 { 30 min = dis[j]; 31 minx = j; 32 } 33 } 34 book[minx] = 1; 35 sum++; 36 j = first[minx]; 37 while (j != -1) 38 { 39 if (dis[y[j]] > dis[minx] + z[j]) 40 { 41 dis[y[j]] = dis[minx] + z[j]; 42 } 43 j = next[j]; 44 } 45 } 46 for (i = 1;i <= n;i++) 47 printf("%d ",dis[i]); 48 printf("\n"); 49 return 0; 50 } 51
三、Bellman-Ford(解决负权边)
Dijkstra也不能解决带有负权边(边的权值为负数)的图。
Bellman-Ford:思想上和代码实现上都勘称完美的最短路径算法
核心代码只有4行,并能完美解决带有负权边的图。
for(k=1;k<=n-1;k++)//进行n-1轮松弛
for(i=1;i<=m;i++)//枚举每一条边
if(dis[v[i]]>dis[u[i]]+w[i])//尝试对每一条边进行松弛
dis[v[i]]=dis[u[i]]+w[i];
问题
1号点到各个点的最短路径
输入:
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
输出:
0 -3 -1 2 4
1 #include <stdio.h> 2 #define Max 10000 3 int main() 4 { 5 int n, m, x[10], y[10], z[10], dis[10]; 6 int i, j, k; 7 scanf("%d%d",&n,&m); 8 for (i = 1;i <= m;i++) 9 scanf("%d%d%d", x + i,y+i,z+i); 10 for (i = 1;i <= n;i++) 11 dis[i] = Max; 12 dis[1] = 0; 13 for (k = 1;k <= n - 1;k++) 14 { 15 for (i = 1;i <= m;i++) 16 { 17 if (dis[y[i]] > dis[x[i]] + z[i]) 18 { 19 dis[y[i]] = dis[x[i]] + z[i]; 20 } 21 } 22 } 23 for (i = 1;i <= n;i++) 24 printf("%d ",dis[i]); 25 printf("\n"); 26 return 0; 27 }
检测负回路,时间上的简单优化
1 #include <stdio.h> 2 #define Max 1000 3 int main() 4 { 5 int n, m, x[10], y[10], z[10]; 6 int dis[10],temp[10], i, j, k, flag,check; 7 scanf("%d%d", &n, &m); 8 for (i = 1;i <= m;i++) 9 scanf("%d%d%d", x + i, y + i, z + i); 10 for (i = 1;i <= n;i++) 11 dis[i] = Max; 12 dis[1] = 0; 13 for (k = 1;k <= n - 1;k++) 14 { 15 for (i = 1;i <= n;i++) 16 temp[i] = dis[i]; 17 for (i = 1;i <= m;i++) 18 { 19 if (dis[y[i]] > dis[x[i]] + z[i]) 20 dis[y[i]] = dis[x[i]] + z[i]; 21 } 22 check = 0; 23 for (i = 1;i <= n;i++) 24 { 25 if (temp[i] != dis[i]) 26 { 27 check = 1;break; 28 } 29 } 30 if (check == 0) 31 break; 32 } 33 flag = 0; 34 for (i = 1;i <= m;i++) 35 { 36 if (dis[y[i]] > dis[x[i]] + z[i]) 37 { 38 flag = 1; 39 break; 40 } 41 } 42 if (flag == 1) 43 printf("此图有负回路\n"); 44 else 45 { 46 for (i = 1;i <= n;i++) 47 printf("%d ", dis[i]); 48 printf("\n"); 49 } 50 return 0; 51 }
队列优化,使用邻接表
1 #include <stdio.h> 2 #define Max 1000 3 int main() 4 { 5 int n, m, x[10], y[10], z[10]; 6 int dis[10], book[10] = {0}, i, j, k, first[10], next[100]; 7 int que[100], tail = 1, head = 1; 8 scanf("%d%d", &n, &m); 9 for (i = 1;i <= n;i++) 10 first[i] = -1; 11 for (i = 1;i <= m;i++) 12 { 13 scanf("%d%d%d", x + i, y + i, z + i); 14 next[i] = first[x[i]]; 15 first[x[i]] = i; 16 } 17 for (i = 1;i <= n;i++) 18 dis[i] = Max; 19 dis[1] = 0; 20 que[tail] = 1; 21 book[1] = 1; 22 tail++; 23 while (head < tail) 24 { 25 k = first[que[head]]; 26 while (k != -1) 27 { 28 if (dis[y[k]] > dis[x[k]] + z[k]) 29 { 30 dis[y[k]] = dis[x[k]] + z[k]; 31 if (book[y[k]] == 0) 32 { 33 que[tail] = y[k]; 34 tail++; 35 book[y[k]] = 1; 36 } 37 } 38 k = next[k]; 39 } 40 book[head] = 0; 41 head++; 42 } 43 for (i = 1;i <= n;i++) 44 printf("%d ", dis[i]); 45 printf("\n"); 46 47 return 0; 48 }

浙公网安备 33010602011771号