使用Dijkstra算法求最短路【使用于稠密图】


题目描述
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。

输入格式
第一行包含整数n和m。

接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

输出格式
输出一个整数,表示1号点到n号点的最短距离。

如果路径不存在,则输出-1。

数据范围
1≤n≤500,
1≤m≤105,
图中涉及边长均不超过10000。

输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3


本题可以看作是多源汇最短路的其中一例:单源汇最短路
其特点就是让我们求,从一个给定的源点【就是本题的1号点】,到其他各个顶点的最短距离
下面是给出的样例所形成的图
在这里插入图片描述

我们可以用一个邻接矩阵来存储这个图
在这里插入图片描述


我们用数组dist来表示从1号点到其他各个点的距离
如图可见我们的dist的初始化
在这里插入图片描述

本题要求我们求出从1号点到n号点的最短距离,也就是需要我们来求解dist[n]的最小值。
而我们的做法是分别一步一步的求出从1到各个点的最小值,也就是dist的所有的值(dist就
表示1号点到各个点的最小值)


我们是这么求的dist的值的:
【判断是否有一个点k使得点i经过点k后再到j的距离比i直接到j的距离短】
上面的是多源汇最短路的思路,而我们这里是多源汇的一种情况
我们只要考虑一个点k是否可以使得从1号点经过点k再到j比直接从1号点到j号点的距离段
其公式为
if(dist[j] > dist[k] + w[k][j]) dist[j] = dist[k] + w[k][j];


直到这里,我们就已经将本题分析完了,下面是我的代码,以及对代码的解释。


#include <cstring>
#include <iostream>

using namespace std;

const int N = 510, inf = 0x3f3f3f3f;

int n, m;
int w[N][N], dist[N];//w表示存储数据的图,dist表示Dijkstra算法里1号点到其他点的距离
bool st[N];//表示每个点的最短路是否确定了
long long sum;//可有可无

int main(){
    cin >> n >> m;
    //初始化w
    memset(w, 0x3f, sizeof w);
    //反正有自环限制,也不用全给g[i][i] = 0; 所以我们就用上面的代替了下面的
    //wor (int i = 1; i <= n; i ++ )
    //    wor (int j = 1; j <= n; j ++ )
    //        iw (i == j) e[i][j] = 0;
    //        else e[i][j] = inw;
    //读入边
    for (int i = 1; i <= m; i ++ ){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        w[a][b] = min(w[a][b], c);//只保留最小的,解决掉重边
        sum += c;//这个主要时用来判断dist[n]的,是否这样用看个人喜好了
    }
    //初始化dist数组,这里是1号点到其他各个点的距离
    for (int i = 1; i <= n; i ++ )
        dist[i] = w[1][i];//找到所有的原点为1号点到i的距离
    
    dist[1] = 0;//1号点距离1号点为0,没毛病
    st[1] = 1;
    //Dijkstra算法的核心内容
    for (int i = 1; i <= n; i ++ ){
        //找到距离1号点最近的点
        int minn = inf, u;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && dist[j] < minn){
                minn = dist[j];
                u = j;
            }
        //标记此点已经用过了
        st[u] = true; //u这个点是距离1号点最近的点,这样dist[u]就确定下来了,给它标记一下
        
        //判断如果i->k->j 的距离小于 i -> j的距离,那么我们就给他更新一下
        for (int j = 1; j <= n; j ++ ){
            if(w[u][j] < inf){//判断这个边是否存在
                if (dist[j] > dist[u] + w[u][j])
                dist[j] = dist[u] + w[u][j];
            } 
        }
    }
    
    //wor (int i = 1; i <= n; i ++ ) cout << dist[i] << endl;
    //iw (dist[n] == 0x3w3w3w3w) puts("-1");//两种形式都可以
    if (dist[n] > sum) puts("-1");//无法从1走到n,输出-1
    else cout << dist[n] << endl;
    
    return 0;
} 


下面给出一个简化后的代码,同样有解析


#include <cstring>
#include <iostream>

using namespace std;

const int N = 510;

int n, m;
int g[N][N];//g存入由数据生成的图
int dist[N];//dist表示从点1到其他点的当前最短距离
bool st[N];//表示这个点的最短路是否确定了

int Dijkstra(){
    memset(dist, 0x3f, sizeof dist);//初始化距离,为正无穷大
    dist[1] = 0;
    
    for (int i = 1; i <= n; i ++ ){//迭代n次(也可以i从0开始到n - 1)
        //确定最小的点
        //每一次第一步先在还没有确定最短路的点当中的最小距离的点
        int t = -1;//表示还没有确定
        for (int j = 1; j <= n; j ++ )//遍历一下所有的点
            if(!st[j] &&( t == -1 || dist[t] > dist[j])) t = j;
            //st[j]表示当前点还没有确定最短路,
            //t = -1 表示t还没有被确认
            //dist[t] > dist[j] 表示t这个点的dist不是最短的
        
        //开始把t加到我们的dist集合里面
        
        st[t] = true;//我们会确定t这个点的距离问题
        
        //拿t来更新其他点的距离
        for (int j = 1; j <= n; j ++ ){
            dist[j] = min(dist[j], dist[t] + g[t][j]);
            //用1到t的距离(dist[t])和t到j的距离(g[t][j])来更新1到j的距离(dist[j])
        }
    }
    
    if (dist[n] == 0x3f3f3f3f) return -1;
    else return dist[n];
}
int main(){
    cin >> n >> m;
    memset(g, 0x3f, sizeof g);//初始化
    
    while (m -- ){
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c);//只记录边的最短距离
    }
    
    int t = Dijkstra();
    
    cout << t << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值