Dijkstra算法——计算一个点到其他所有点的最短路径的算法

迪杰斯特拉算法百度百科定义:传送门

gh大佬博客:传送门

迪杰斯特拉算法用来计算一个点到其他所有点的最短路径,是一种时间复杂度相对比较优秀的算法 O(n2)(相对于Floyd算法来说)

是一种单源最短路径算法,但是它并不能处理负边权的情况

Dijkstra的算法思想:①将一开始所有的非源点到源的距离设置成无限大(你认为的无限大实际上是0x3f(int)或者0x7fffffff(long long)),然后源到源距离设置成0(不就是0吗),然后每次找到一个距离源最短的点u,将其变成白点,枚举所有的蓝点,如果源到白点存在中转站——一个蓝点使得源->蓝点和蓝点->白点的距离和更短,就更新。②每找到一个白点,就尝试更新其他蓝点,直到更新完毕。

代码及注释:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<time.h>
#include<queue>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pr;
const double pi=acos(-1);
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define Rep(i,u) for(int i=head[u];i;i=Next[i])
#define clr(a) memset(a,0,sizeof a)
#define pb push_back
#define mp make_pair
#define fi first
#define sc second
ld eps=1e-9;
ll pp=1000000007;
ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
ll read(){
    ll ans=0;
    char last=' ',ch=getchar();
    while(ch<'0' || ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}//快读
//head



const int maxn=5001;
int g[maxn][maxn];//g数组用来存储图; 
int n,m,s;//分别表示点的个数、有向边的个数、出发点的编号;
bool vis[maxn];//表示是否已经到达过;
int d[maxn];//d[i]表示从询问点到点i的最短路径;
const int inf=2147483647;

int main ()
{
    n=read(),m=read(),s=read();
    rep(i,1,n)
    {
        d[i]=inf;
        
        rep(j,1,n)
            g[i][j]=inf;
            
        g[i][i]=0;//自己到自己的最短路径当然是0 
    }//初始化数组; 
    
    rep(i,1,m)
    {
        int u=read(),v=read(),w=read();
        //u,v,i分别表示第i条有向边的出发点、目标点和长度;
        g[u][v]=w;//读入; 
    }
    
    vis[s]=1;//将起点标记成已经到达;
    
    rep(i,1,n)
        d[i]=g[s][i];//将最短路径初始化;
        //如果两点之间有路线就初始化为该距离,如果没有就还是inf;
        
    while(1)
    {
        int stt_node=0,stt_dis=inf;//stt=shortest 初始化两个变量 
        // stt_node表示最短路径的终点,stt_dis表示最短路径的长度 
        
        rep(i,1,n)
        { 
            if(vis[i]==0&&d[i]<stt_dis)
            //如果该点还没有到达,并且他的距离小于最短距离 
            {
                stt_node=i,stt_dis=d[i];//更新变量 
            }
        }
        
        if(stt_node==0) break;
        //如果已经没有可以更新的最短路径了,就说明已经结束了
        
        vis[stt_node]=1;//将该点标记成已经到达 
        
        rep(i,1,n)
        {
            if(vis[i]||g[stt_node][i]==inf)continue;
            //如果并没有到达或者是两点之间没有路径,就跳出循环 
            
            d[i]=min(d[i],stt_dis+g[stt_node][i]);//更新最短路径 
        }
    }
    
    rep(i,1,n)
        printf("%d ",d[i]);
    return 0;
}

我们考虑一下对它的优化。因为如果我们每一次都要扫一遍判断出边,我们还不如直接存出边:

邻接表!(链式前向星)

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;
const int INF = 2147483647;//最大化初始值 
struct edge
{
    int to, dis_, next;//出边的终点、出边的长度、下一条出边
} Edge[9999999];//存储边 
struct node
{
    int to, dis;
    inline friend bool operator<(const node &a, const node &b)
    {
        return a.dis < b.dis;//比较函数 
    }
};
int head[9999999], dis[9999999];
//head用来存储每个点的最后一条出边,dis表示源点到某个点的最短路径 
bool vst[9999999];//记录是否已经到达过
int nodenum, edgenum, origin_node, cnt = 1;
//分别表示点的个数、有向边的个数、出发点的编号 
priority_queue<node> q;

inline void add_edge(int from, int to, int value)//添加边 
{
    Edge[cnt].to = to;//起点 
    Edge[cnt].dis_ = value;//长度 
    Edge[cnt].next = head[from];//连接到上一条from的出边
    head[from] = cnt++;
    //将head[from]——from的最后一条出边改成这个边,然后到下一条边的下标(下标++
}//用来存图 

inline void dijkstra()
{
    for (register int i = 1; i < origin_node; i++)
    {
        dis[i] = INF;
    }
    //dis[origin_node]=0;
    for (register int i = origin_node + 1; i <= nodenum; i++)
    {
        dis[i] = INF;
    }//除了源点其他最短路径都初始化为INF 
    q.push((node){origin_node, 0});//入队 
    while (!q.empty())
    {
        int x = q.top().to;
        q.pop();
        if (vst[x])//如果都到达过,就跳出循环 
            continue;
        vst[x] = 1;
        for (register int i = head[x]; i; i = Edge[i].next)
        {
            dis[Edge[i].to] = min(dis[Edge[i].to], dis[x] + Edge[i].dis_);//更新dis的值 
            q.push((node){Edge[i].to, -dis[Edge[i].to]});//出队 
        }
    }
}

template <typename T_>
inline T_ getnum()//读入数据 
{
    T_ res = 0;
    bool flag = false;
    char ch = getchar();
    while (!isdigit(ch))
    {
        flag = flag ? flag : ch == '-';
        ch = getchar();
    }
    while (isdigit(ch))
    {
        res = (res << 3) + (res << 1) + ch - '0';
        ch = getchar();
    }
    return flag?-res:res;
}
template<typename T_>
inline void putnum(T_ num)
{
    if (num<0)
    {
        putchar('-');
        num=-num;
    }
    if (num>9)putnum(num/10);
    putchar('0'+num%10);
}

int main()
{
    nodenum = getnum<int>(), edgenum = getnum<int>(), origin_node = getnum<int>();//读入 
    for (register int i = 1; i <= edgenum; i++)
    {
        register int f, t, v;
        f = getnum<int>(), t = getnum<int>(), v = getnum<int>();
        add_edge(f, t, v);
    }
    dijkstra();
    for (register int i=1;i<=nodenum;putchar(' '),i++)
    {
        putnum<int>(dis[i]);//输出 
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lcezych/p/10739866.html