[题]Dijkstra求最短路 I / II —— 标签 #单源最短路 #Dijkstra

最短路学习思路图


Dijkstra求最短路 I

一、Dijkstra求最短路 I (朴素)

就是说,从目标点开始,利用被处理过的当前距离最小的点,不断地对为处理过的点进行处理,直到所有点都处理完毕。
也就是说,这是针对的操作

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int n, m, g[N][N], djs[N];
bool s[N];

int djst(){
    memset(djs, 0x3f, sizeof djs);
    djs[1] = 0;
    //下面这个循环,循环的是次数,有n个点,所以要进行n次处理
    for(int i = 1; i <= n; i ++){
        int t = -1;
        //这里循环是为了找到未被处理的点中,距离最小的点
        for(int j = 1; j <= n; j ++)
            if(!s[j] && (t == -1 || djs[t] > djs[j]))
                t = j;
        //对将要进行操作的点进行标记
        s[t] = 1;
        //利用它来不断更新其他点的距离(即使是已经操作过的点也没有关系,因为肯定不会更新那些已经操作过的点)
        for(int j = 1; j <= n; j ++)
            djs[j] = min(djs[j], djs[t] + g[t][j]);
    }
    //如果我们要求的点没有办法到达源点,那就返回-1
    if(djs[n] == 0x3f3f3f3f) return -1;
    //否则如实输出就可以了
    return djs[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);
    }
    cout << djst();
    return 0;
}

二、Dijkstra求最短路 II(堆优化)

Dijkstra求最短路 II

  1. 优先队列的函数补充:
    优先队列数据结构(堆)的函数用法 20.10.17
  2. 可以更新的地方分析:循环 n2 次来找点 t;
    用小根堆堆来做可时间O(n),也就是用优先队列,优化后为O(log n)
	就是这里可以优化
 for(int j = 1; j <= n; j ++)					// *3 
            if(!s[j] && (t == -1 || djs[t] > djs[j]))	// *3 
                t = j;			
  1. 将t加入s; ——————————————————这里是n次
  2. 用t更新其它点的距离; —————————————具体操作时m次,遍历所有的边来更新
    堆优化后 整个计算量最大是 O(m log n)。
#include<bits/stdc++.h>
using namespace std;
const int N = 150010;
int  n, m;
int dist[N];
bool st[N];
int ne[N], h[N], e[N], w[N], idx = 0;
typedef pair <int , int > P;
//邻接矩阵
void add(int a, int b, int c){
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
//堆优化版……
int djs(){
    memset(dist , 0x3f, sizeof dist);
    dist[1] = 0;
    //下面这句话就按这种格式来写,具体为什么我不知道……
    priority_queue < P, vector<P>, greater<P> > he;
    //优先队列的插入方式
    //记得我们这里用的是pair类型的,其中第一个代表距离,因为优先队列会自动按照第一个元素first进行排序
    //第二个很明显就是点的编号了
    he.push({0, 1});
    
    while(he.size()){
    //取出头结点
        P t = he.top();
    //头结点出队!
        he.pop();
    //ver是节点编号,dis是距离
        int ver = t.second, dis = t.first;
    //确定是没有操作过的点
    //因为一个点有可能会进队很多很多次,因为每一次被更新都会进队,不过我们只要取距离最短的那次。
        if(st[ver]) continue;
	//对将要处理的点进行标记
        st[ver] = 1;
    //将所有与当前节点有关联的都遍历一遍,依次更新那些点的距离
        for(int i = h[ver]; i != -1; i = ne[i]){
        //j是遍历到得点的编号
            int j = e[i];
            //如果有必要我们才进行更新
            if(dist[j] > dis + w[i]){
                dist[j] = dis + w[i];
                /*
                之所以一更新完就直接放进优先队列,
                //一是因为它会自动排序,
                //二是因为当前点已经是离远点最最近的还未被处理完的点,
                	那么下一个离源点最近的还未被处理过的点一定会与当前正在处理的点有关
					当然,如果与源点不连通的话也就无法遍历到,没办法的事……
				//更新后的距离,以及节点编号
				*/
                he.push({dist[j], j});
            }
        }
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main(){
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i ++){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    cout << djs() << endl;
    return 0;
}

*1 强调:“>”不要两个拼在一起。
less是从大到小,greater是从小到大。
第一个参数为数据类型。
第二个参数为容器类型。
第三个参数为比较函数。
*2 千万不要忘记。

没有废话版:

I

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n, m, g[N][N], djs[N];
bool s[N];

int djst() {
    memset(djs, 0x3f, sizeof djs);
    djs[1] = 0;
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!s[j] && (t == -1 || djs[t] > djs[j]))
                t = j;
        s[t] = 1;
        for (int j = 1; j <= n; j++) djs[j] = min(djs[j], djs[t] + g[t][j]);
    }
    if (djs[n] == 0x3f3f3f3f)
        return -1;
    return djs[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);
    }
    cout << djst();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值