第六章 最短路径——有向图(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 }
View Code

 二、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 }
View Code

使用邻接表存储边,针对稀疏图,此时的存储方式,节约空间

 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  
View Code

三、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 }
View Code

检测负回路,时间上的简单优化

 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 }
View Code 

队列优化,使用邻接表

 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 }
View Code

 

posted @ 2017-06-25 16:18  daisy_ding  阅读(284)  评论(0)    收藏  举报