求最短路径

130 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第25天,点击查看活动详情

已知如下带权有向图,从A到B最短路径怎么求呢? image.png

这个题叫做:多源最短路径问题。

图上有4个点,8条边,我们可以用一个4*4的数组来存储两个点能否到达,自己到自己,路径为0,不可到达,路径为无穷大,比如1-》2长度为2,及数组名字[1][2]=2;2到4没有直接路径,所以数组名字【2】[4]=∞。那么上图可以抽象成

image.png

现在回到问题:如何求任意两个点之间的最短路径?我们可以通过n^2遍深度或者广度优先搜索,即对每一个点进行一次深度或者广度优先搜索,就可以求出任意两点的最短路径。这个放在后面讲解,那么我们还可以用什么方法呢?

要让两个点之间路程变短,只能引入前提点,即通过 点A-》其他点-》点B 才能缩短A到B的路程。那么对应其他的每一个点,都可能让除了他的另外两个节点之间路程变短。

当只能通过A直接到达B,那么上面的图就是结果,如果允许通过A-》节点1——》B那么结果如下

image.png

那么代码如何实现? 下面表示只能通过节点1中转,我们只需要判断通过1和不通过1的路程大小就可以了

for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(e[i][j]>e[i][1]+e[1][j])
  e[i][j]=e[i][1]+e[1][j];
}
}

那么通过二号节点,就可以

image.png 同理可得通过其他节点的。

那么允许通过12节点的代码怎么写?

image.png 结果是:

image.png

同理,可得通过1,2,3,4中转的,任意两点间最终距离为:

image.png

核心代码五行:

for(int k=1;k<=n;k++){
   for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
      if(e[i][j]>e[i][k]+e[k][i]){
          e[i][j]=e[i][k]+e[k][i]
      }
      }
      }
}

基本思想就是,最开始允许通过1中转,后来可以通过2,3.等。求任意两点之间最短距离,其实是一种动态规划思想。即每一步都拿到最小值。

深度优先遍历:前面用数组存储都是类似的。

如何找从1号城市到4号城市的最短路径? 1-》4

1-》2-》3-》4

1-》2-》3-》1-》4 x

可以看出,两种符号的路径里面,直接1-》4最短。

所以dfs如下:

//cur当前城市编号   dis已经走过的路程
void dfs(int cur,int dis){
int j;
//如果已经走过路程大于已经找到的最短路,就表示得到了答案
if(dis>min){
return;
}
//判断是不是到达了目标城市
if(cur==n){
if(dis<min) min=dis;
return ;
}
for(int j=1;j<=n;j++){
//判断当前城市,到目的j是不是有路,并且这个路,是不是已经走过
if(e[cur][j]!=Integer_MAX && book[j]==0){
book[j]=1;//表示当前城市已经在路径
dfs(j,dis+e[cur][j]);//从j出发,继续
book[j]=0;
}
}
}