赞
踩
Floyd算法本质上是动态规划,即对于每一对结点,不断更新其以某一结点为中间结点的最小值。关于算法理解可以参考以下两篇文章:
Floyd算法的详细过程
理解为什么每次循环时d[i][j]一定取到了最小值
代码即为简单的三重循环
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[i][j]>d[i][k]+d[k][j])
d[i][j]=d[i][k]+d[k][j];
时间复杂度:O(N^3)
空间复杂度:O(N^2)
适用于全源最短路问题(能确定所有结点之间的最短路)
在大多数机试时间范围内,使用Floyd算法要求图的大小不超过200个结点
可以看出,Floyd算法实现起来非常方便,但是由于其计算了所有节点之间的最短路,因此浪费了不少时间。所以,接下来介绍Dijkstrta算法。
Dijkstrta算法算法的基本思想是,从起点开始作为标准点,选择与之距离最近的点作为新的标准点,然后更新其他点至起点的最短距离,依次选择至最后一个点。
图片来源点击跳转
时间复杂度:O(N^2)(堆优化后可以降至O(N*logN))
空间复杂度:O(N)
适用于单源最短路问题
下面是一道王道中的简单例题,分别使用两种算法来解决,加深一下对于两种算法的理解:
Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
Sample Output
3
2
使用Floyd解法:
#include<iostream> using namespace std; int ans[101][101]; int main() { int n, m; while (scanf("%d%d", &n, &m) != EOF) { if (n == 0 && m == 0) break; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { ans[i][j] = -1; // 对邻接矩阵初始化,我们用-1代表无穷 } ans[i][i] = 0; // 自己到自己的路径长度设为0 } while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); ans[a][b] = ans[b][a] = c; } for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (ans[i][k] == -1 || ans[k][j] == -1) continue; if (ans[i][j] == -1 || ans[i][k] + ans[k][j] < ans[i][j]) ans[i][j] = ans[i][k] + ans[k][j]; } } } printf("%d\n", ans[1][n]); } return 0; }
使用Dijkstra解法:
#include <vector> #include <iostream> using namespace std; struct E { // 邻接链表中链表元素结构体 int next; // 代表直接相邻的节点 int c; // 代表该边的权值(长度) }; vector<E> edge[101]; // 邻接链表 bool mark[101]; // 标记,当mark[j]为true时表示节点j的最短路径长度已经得到,该节点已经加入集合K /*距离向量,当mark[i]为true时,表示已得到的最短路径长度; 否则,表示所有从节点1出发,经过已知的最短路径达到集合K中的某节点,再经过一条边到达节点i的路径中最短的距离 */ int Dis[101]; int main() { int n, m; while (scanf("%d%d", &n, &m) != EOF){ if (n == 0 && m== 0) break; for (int i = 1; i <= n; i++) edge[i].clear(); // 初始化邻接链表 while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); E tmp; tmp.c = c; tmp.next = b; edge[a].push_back(tmp); tmp.next = a; edge[b].push_back(tmp); } for (int i = 1; i <= n; i++) { // 初始化 Dis[i] = -1; // 所有距离为-1,即不可达 mark[i] = false; // 所有节点不属于集合K } Dis[1] = 0; // 得到最近的点为节点1,长度为0 mark[1] = true; // 将节点1加入集合K int newP = 1; // 集合K中新加入的点为节点1 for (int i = 1; i < n; i++) { // 循环n-1次,按照最短路径递增的顺序确定 for (int j = 0; j < edge[newP].size(); j++) { int t = edge[newP][j].next; // 该边的另一个节点 int c = edge[newP][j].c; // 该边的长度 if (mark[t] == true) // 若另一节点也属于K,则跳过 continue; // 若该店尚不可达,或者该节点从新加入的节点经过一条边到达时比以往距离更短 if (Dis[t] == -1 || Dis[t] > Dis[newP] + c) Dis[t] = Dis[newP] + c; } int min = 123123123; // 最小值初始化为一个大整数,为找最小值做准备 for (int j = 1; j <= n; j++) { if (mark[j] == true) continue; if (Dis[j] != -1 && Dis[j] < min) { min = Dis[j]; newP = j; } } mark[newP] = true; } // printf("%d %d\n", Dis[T], cost[T]); // 输出答案 } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。