Dijkstra

最短路径

 

dijkstra

不可解决有负权的图

但是若是没有负权的话,最好用这个,而不要用spfa,因为spfa的时间复杂度极其不稳定

对于稀疏图来说,spfa的时间复杂度确实是要比dijkstra要低

但,若是稠密图的话,spfa的时间复杂度就极其不可观了

所以,dijkstra的重要性显然

 

1.定义

    不可解决有负权边的图

    时间复杂度为O(n2)

    经过堆优化的dijkstra的时间复杂度为O(nlogn)

 

2.算法描述

    贪心的思想(这是因为这个思想,所以判不出来负权边--(咳咳,个人之见,逃~)

    以起始点为中心向外层层扩展(广搜的思想),直到扩展到终点为止

基本思想:

    引入两个集合S和U。

    S:记录已求出最短路径的顶点

    U:记录未求出最短路径的顶点

    (能到源点的所有点)

操作:

    1.初始时,S集合中只包含源点s

         U集合中包含除s以外的其他顶点 且 U中顶点的距离为“起点s到该顶点的距离”

         (若相连,即为边权值;若不相连,则设其为无穷大)

    2.从U中选出“距离最短的顶点k”,将k加入s中,并将k从U集合中移出

    3.用新加入的顶点k来更新U集合中各个顶点到起点s的距离

     之所以更新U中顶点的距离,是因为 上一步中确定了k是求出最短路径的顶点,从而可以用k来更新其他的值

    4.重复2.3,直到遍历完所有点

 

 

(假装已经会了)

 

普普通通的dijkstra

 

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int map[110][110];//这就是map数组,存储图
int dis[10010];//dis数组,存储估计值
int book[10010];//book[i]代表这个点有没有被当做源点去搜索过,1为有,0为没有。这样就不会重复搜索了。
int n,m;

void dijkstra(int u)//主函数,参数是源点编号
{
    memset(dis,88,sizeof(dis));//把dis数组附最大值(88不是十进制的88,其实很大)
    int start=u;//先从源点搜索
    book[start]=1;//标记源点已经搜索过
    for(int i=1; i<=n; i++)
    {
        dis[i]=min(dis[i],map[start][i]);//先更新一遍
    }
    for(int i=1; i<=n-1; i++)//源点不用 
    {
        int minn=9999999;//这里的minn不是题解上的minn,这代表的是最近点到源点的距离,start才代表最近的点、
        for(int j=1; j<=n; j++)
            if(book[j]==0 && minn>dis[j])
            {
                minn=dis[j];
                start=j;//找到离源点最近的点,然后把编号记录下来,用于搜索。
            }
        book[start]=1;
        for(int j=1; j<=n; j++)
            dis[j]=min(dis[j],dis[start]+map[start][j]);//以新的点来更新dis。
    }
}

int main()
{
    cin>>n>>m;
    memset(map,88,sizeof(map)); 
    for(int i=1; i<=m; i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        map[a][b]=c;
    }//有向图吧 
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(i==j)
                map[i][j]=0;//仅是初始化 
    dijkstra(1);//以1为源点。
    for(int i=1; i<=n; i++)
        cout<<dis[i]<<" ";
}

 (普普通通的dij也几乎过不了什么,还得加上一个链式前向星)

 

堆优化的dijkstra

 

1.基本思想

 

//堆优化dijkstra 边权不可为负 
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;

inline int read()//快读 
{
    int sum = 0,p = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '0')
            p = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (sum *= 10) += ch - '0';
        ch = getchar();
    }
    return sum * p;
}

const int maxn = 1e5 + 5;
const int maxm = 2e5 + 5;
int n,m,s;//n为点的个数,m为变得个数,s为源点 
int tot,dis[maxn],wei[maxm];//dis为i点到源点的距离,wei为边权 
int head[maxn],to[maxm],nxt[maxm];//链式前向星 
bool mrk[maxn];//标记该点是否被加入到最短路径的生成树中(0-没有,1-有) 
priority_queue< pair<int,int> > q;
/*
优先队列:默认是大根堆
pair第一维为dis[i]的相反数(这样就可以变成小根堆啦)
第二维为节点编号 
*/

void dijkstra()
{
    memset(dis,0x3f,sizeof(dis));
    dis[s] = 0;//dis初始化 起点为0,其余为正无穷
    q.push(make_pair(0,s));
    while(q.size())
    {
        int x = q.top().second;
        q.pop();//取堆顶
        if(mrk[x])
            continue;
        mrk[x] = true;
        for(int i = head[x];i;i = nxt[i])
        {//扫描所有出边
            int y = to[i];
            int z = wei[i];
            if(dis[y] > dis[x] + z)
            {
                dis[y] = dis[x] + z;
                q.push(make_pair(-dis[y],y));
            }
        } 
    }
}

int main()
{
    n = read(),m = read(),s = read();
    int x,y,z;
    for(int i = 1;i <= m;i++)
    {
        x = read(),y = read(),z = read();
        wei[++tot] = z;
        nxt[tot] = head[x];
        to[tot] = y;
        head[x] = tot;
    }//构建邻接矩阵 (通常可用add函数) 
    dijkstra();
    for(int i = 1;i <= n;i++)
        printf("%d ",dis[i]);
    return 0;
}

 

 

 

(我应该会回来补一补的qwq...)

 

 

 

    

    

    

 

posted @ 2019-03-18 20:24  darrrr  阅读(1909)  评论(0编辑  收藏  举报