三、最短路径算法
对于带权值的图,从一点到另外一点的权值和(距离和)最小是多少,最短路径算法讨论的就是这样的问题
提前说一句,负权值的回路是没有最短路的,因为每绕一圈最短路都会减少
也补一句,所谓最短路径的最优子结构,就是说设s[i][j]是从i到j的权值和,当s[i][j]最小的时候记为p[i][j],那么这条路所经过的k,满足p[i][j]=p[i][k]+p[k][j],(注意k在i->j的路径上,这一点很重要,很多博客举a地到b地的最短距就是a地到c地的最短距加上c地到b地的最短距都是很明显的错误说法,想想三角形的三边。。。)用反证法是很容易证明的
1.Floyd-Warshall
①思路:对于两点之间的最短路,存在经过某一点中转和不经过该点中转两种选择,我们通过对这两种做法的选择判断来更新已有的最短路,当所有的情况都分析过之后,留下的便是最短路
②实现:用dis[num][num]存最短路,一开始dis里面存的都是初始化时得到的权值,先看所有的最短距用第0个节点中转是否会产生更小的距离(比如第i个节点到第j个节点dis[i][j]?dis[i][0]+dis[0][j]),更小的话,就更新;再看第1个节点,这个时候直接如法炮制,在上一次处理过的矩阵上重复操作(dis[i][j]?dis[i][1]+dis[1][j])即可;在之后以此类推,直到每一个点作为中转的情况都考虑到,得到的就是最短路
③说明:为什么可以在之前更新过的矩阵上直接开始下一轮更新?比如dis[i][1]+dis[1][j],在讨论是否i->1->j比i->j更好的时候,其实i->0->1 and i->1 ; 1->0->j and 1->j孰大孰小我们已经判断过了,这样对于i->j的最短路可能情况,在我们已经讨论的中转点里(0 和1)里面我们只用讨论1了,之后的分析以此类推,可以说明这样做的正确性
④代码:
int dis[num][num],G[num][num];
int main(){
//省略输入数据初始化图矩阵的过程
for(int i=0;i<num;i++)
for(int j=0;j<num;j++)
dis[i][j]=G[i][j];
//Floyd
for(int k=0;k<num;k++){//枚举中转点
for(int i=0;i<num;i++){//这两重循环是更新最短距的
for(int j=0;j<num;j++){
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
return 0;
}
⑤时间复杂度:三种循环,假设点数是N,时间复杂度为O(N^3)
⑥局限性:虽然实现容易,时间复杂度太大
2.Dijkstra
假设一个图里面没有负权边,固定一个起点讨论最短路问题
①思路&#x