塔塔露也能学会的算法(1) | dijkstra从入门到放弃

本文详细介绍了Dijkstra单源最短路径算法的基本原理和实现过程,包括邻接表存储、颜色标记和距离更新。同时探讨了算法的优化技巧,如链式前向星存储、重载运算符以及使用优先队列进行堆优化。通过实例分析了P3371题目,展示了优化后的Dijkstra算法在解决实际问题中的高效性。
摘要由CSDN通过智能技术生成

1. 基础

塔塔露也能学会的dijkstra算法
Dijkstra,单源最短路径算法,要求图中无负环。

  • 过程:选点(Black),找周围点(Grey),松弛,选点(Black),找周围点(Grey),松弛,……,直到没有点。

  • 使用邻接表map[maxn][maxn]存储边关系,bool型数组存储颜色 (比如1=white, 0=black/grey),dist[maxn]存储源点到各个点的距离。

  • 通过判断dist[i]是否为INF可以得出该点是grey点还是white点。

  • 拓展周围点时的距离关系:dist[i]=dist[pre]+map[pre][i]

例题1P1359 租用游艇

裸的dijkstra可以直接过,塔塔露听了都要落泪。甚至代码都没有看的必要。

2. 优化

1)链式前向星

一种存表方式,在代码好写和效率高之间中规中矩。 静态建邻接表,时间效率为O(m),空间效率也为O(m),遍历效率也为O(m)。

塔塔露也能看懂的链式前向星
2)重载运算符

例如:

struct node {  //定义一个结构体node(节点)
   int x;
   int y;
   int len;   //node中有3个成员变量x,y,len
   inline bool operator <(const node &a)const {
       return len<a.len;
   }
};

重载<操作符。可以对两个node使用<操作符进行比较;括号中的const表示参数a对象不会被修改,最后的const表明调用函数对象不会被修改。

inline :只有当函数非常短小的时候它才能得到我们想要的效果,如果内联函数不能增强性能,就避免使用它。

在函数声明或定义中,函数返回类型前加上关键字inline,即可以把函数指定为内联函数。这样可以解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题。关键字inline必须与函数定义放在一起才能使函数成为内联函数,仅仅将inline放在函数声明前面不起任何作用。inline是一种“用于实现”的关键字,而不是一种“用于声明”的关键字。

3)堆优化

例题是P3371 【模板】单源最短路径(弱化版)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int maxn=10005;
const int maxm=500005;
const int INF=0x3f3f3f;
const int outinf=2147483647;

int n,m;
bool s[maxn];
LL dis[maxn];
struct Edge{
    int to;
    int w;
    int next;
};

Edge edge[maxm];
int head[maxn];
int ctr=0;
void add_edge(int u,int v,int w){
    ctr++;
    edge[ctr].to=v;
    edge[ctr].w=w;
    edge[ctr].next=head[u];
    head[u]=ctr;
}//链式向前星存法

struct node{
    int w,now;
    inline bool operator < (const node &x) const{
        return w>x.w;
    }//重载运算符
};

priority_queue<node>q;//也可以使用pair结构
void dijkstra(int v0){
    int MIN=INF+1;
    dis[v0]=0;//初始化
    q.push((node){0,v0});//从v0-v0的长度为0的边入队,满足循环的起始条件
    while(!q.empty()){
        node x=q.top();//取堆顶边,x是边的编号
        q.pop();//出队
        int u=x.now;//u是编号为x的边的终点
        if(s[u]) continue;//如果该边已涂黑,重来
        s[u]=1;//涂黑
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;//编号为i的边,起点为u,终点为v
            if(dis[v]>dis[u]+edge[i].w){
                dis[v]=dis[u]+edge[i].w;//松弛
                q.push((node){dis[v],v});//入队
            }
        }
    }
}
void prepare(){
    memset(dis,0x3f,sizeof(dis));
    memset(head,-1,sizeof(head));
    memset(s,0,sizeof(s));
    for(int i=1;i<=m;i++) edge[i].w=INF;
}
int main(){
    int v0; prepare();
    scanf("%d%d%d",&n,&m,&v0);
    for(int i=0;i<m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add_edge(u,v,w);

    }
    dijkstra(v0);
    for(int i=1;i<=n;i++){
        if(dis[i]>=INF) printf("%d ",outinf);//不知道为什么这里写==会卡掉一个解
        else printf("%lld ",dis[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值