单源最短路问题复习

在学习单源最短路之前,首先要了解数组和for循环

  • for循环

for循环,顾名思义就是循环语句

运行以下代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    for(int i=0;i<=10;i++){
        cout<<i<<' ';
    }
    return 0;
}

得到

单源最短路问题复习

 

 

 运行以下代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    for(int i=0;i<=10;i++){
        cout<<i<<endl;
        for(int j=0;j<=10;j++){    
          cout<<j<<' ';
        }
        cout<<endl;
    }
    return 0;
}

得到

单源最短路问题复习

 

 

 这两个例子可以直观地理解for循环的运行方式和效果

  • 数组

对于一维数组,形式上可以理解为一个数值可变的数列

对于二维数组,形式上可以理解为一个矩阵

其作用是有序地储存变量,

例子如下

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    int a[10][10];//声明一个10*10的二维数组,仅做储存数据使用,无数学意义 
    memset(a,127,sizeof(a));//将数组中的所有元素全赋值为2139062143
    for(int i=0;i<10;i++){
        for(int j=0;j<10;j++){
            cout<<a[i][j]<<' ';
        }
        cout<<endl;
    }//for循环输出 
    return 0; 
}

运行后输出结果

单源最短路问题复习

 需要注意的是,数组的下标从0开始计算;

如:a[10]中包含的元素为a[0]~a[9]

但是在处理问题的时候通常忽略掉a[0],从a[1]开始初始化

  • 单源最短路问题介绍

单源最短路问题复习

 

对于如图所示的图,字母表示节点,也即地点,其中相连的边表示两点间的距离

单源最短路问题是指以一个节点为起点,求它到其余每个点的最短距离

  • 图的存储方式

在输入时,第一行为n,m,s三个数字,分别表示该图的点数和边数,以及起点;目标是求该起点到其余各点的最短路径

之后m行,每行的第一个数字为该边的起点编号,第二个数字为终点编号,第三个数字为该边的长度

eg:

单源最短路问题复习

 

 

 

 

 

 

我们以数组dis[]表示起点s到每个点的路径长度

初始化时令dis[s]=0

dis[i]=2147483647,其中i<=n且i!=s

(dis[s]即s到自身的距离,为0;而开始时,s到其余各点的距离未知,设为不可到达,用一个很大的数表示即可)

以数组u[],v[],w[],分别表示边i的起点,终点,长度(w表示weight,权重)

则可有以下的输入代码

        int n,m,s,u[500001],v[500001],w[500001]
        cin>>n>>m>>s;
    for(int i=1;i<=m;i++){
        cin>>u[i]>>v[i]>>w[i];
        }
    for(int i=1;i<=n;i++)
        dis[i]=2147483647;
        dis[s]=0;

至此,整个图储存完毕

  • 求起点s到所有点的最短路径

比较好理解的:若存在边(u[j],v[j],w[j])使得dis[v[j]]>dis[u[j]]+w[j]

则说明起点s先到u[j]再到v[j]比s直接到v[j]更快

则令dis[v[j]]=dis[u[j]]+w[j],保证最小

要进行上述操作,则只需运行如下代码

for(int j=1;j<=m;j++){
       if(dis[v[j]]>dis[u[j]]+w[j]){
               dis[v[j]]=dis[u[j]]+w[j];
           }
       }

如果只进行一次上述操作,那么dis[]中存储的结果只会是和s相邻的边(可以在纸上手动运行以下代码),其余边没有进行迭代

为了让除s外的n-1个点都被遍历到,我们需要进行n-1次上述操作,即

for(int i=1;i<=n-1;i++){//该外层i循环在这里只是个计数作用 
	        for(int j=1;j<=m;j++){
	            if(dis[v[j]]>dis[u[j]]+w[j]){
	                dis[v[j]]=dis[u[j]]+w[j];
	            }
	        }
	}

至此,dis[i]中的数均变为s到i点的最短路径

最后输出即可

    for(int i=1;i<=n;i++)
        cout<<dis[i]<<" ";
    return 0;

 

优化先不谈

总体代码如下:

#include<bits/stdc++.h>
using namespace std;
int u[500001],v[500001],w[500001];
long long dis[500001];
int n,m,s;
int main(){
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++)
        dis[i]=2147483647;
        dis[s]=0;
    for(int i=1;i<=m;i++){
        cin>>u[i]>>v[i]>>w[i];
    }
    for(int i=1;i<=n-1;i++){
            for(int j=1;j<=m;j++){
                if(dis[v[j]]>dis[u[j]]+w[j]){
                    dis[v[j]]=dis[u[j]]+w[j];
                }
            }
    }
    for(int i=1;i<=n;i++)
        cout<<dis[i]<<" ";
    return 0;
}

将上述例子中的数代入计算一下(用手)

单源最短路问题复习

得到可以输出dis数组是

0 2 4 3

算法时间复杂度为O(n*m),非常慢,当n*m比较大时计算机无法承受

 

end

上一篇:模板库


下一篇:[cf1599G]Shortest path