【讲解 + 模板】Dijkstra迪杰斯特拉+堆优化

来源:互联网 发布:淘宝预约快递寄件 编辑:程序博客网 时间:2024/05/17 08:29

Dijkstra迪杰斯特拉+堆优化

众所周知,朴素的迪杰斯特拉的时间复杂度为O(n^2),这在某些题目当中是会超时的。但如果在迪杰斯特拉中枚举每个最短边时加入堆优化,则迪杰斯特拉的效率则会大大提高。

朴素版迪杰斯特拉

void dijkstra(int x)  //x为起点,使用迪杰斯特拉查找起点到其他任意点的最短路径{    for(int i = 1; i <= n; i++) dis[i] = w[x][i];  //dis[i]数组存储从x到i的最短路(迪杰斯特拉跑完之前不一定是最终解)    //w[i][j]数组存储从i到j的路径长度(这里用的邻接矩阵存储,也可以换成前向星等)    b[x] = 1; //b[i]数组存储此点是否被访问过    dis[x] = 0;    for(int i = 1; i <= n - 1; i++)      {        minl = maxx;        k = 0;        for(int j = 1; j <= n; j++)  //查找可以更新的点          if((!b[j]) && (dis[j] < minl))            {                minl = dis[j];                k = j;            }        if(k == 0) break;  //这一句也可以换成if(k == y) break;表示已经更新完从x到y的最短路        b[k] = true;  //更新k点,把k标记为已确定最短路径        for(int j = 1; j <= n; j++)  //更新与k相连的每个未确定最短路径的顶点j          if(dis[k] + w[k][j] < dis[j])            dis[j] = dis[k] + w[k][j]; //可以加上pre[j] = k用来记录前驱节点      }}

加入堆优化

笔者在oj上找到了几个较好的不同类型的模板,大家可以灵活选择记忆理解。

题目

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:
第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:
一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)
输入输出样例
输入样例#1:

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出样例#1:

0 2 4 3

版本一(邻接矩阵存图)

#include<vector>#include<map>#include<queue>#include<algorithm>#include<set>#include<iostream>#include<cstdio>#include<list>#include<string>#include<cmath>#include<cstring>#include<stack>using namespace std;struct ha//小根堆{    int x;     int d;//节点编号     bool operator < (const ha &a) const     {        return x > a.x;    }}; struct haha//小根堆{    int x;     int d;//节点编号}; ha e;haha ee;int d[1000010];priority_queue <ha> q;int n, m, s, xx, yy, zz, i;vector<haha> a[1000000];int main(){    scanf("%d%d%d",&n ,&m ,&s);    for(i = 1; i <= m; i++)    {        scanf("%d%d%d",&xx ,&yy ,&zz);        ee.d = yy;        ee.x = zz;        a[xx].push_back(ee);    }    for(i = 1; i <= n; i++) d[i] = 2000003647;    e.d = s;    e.x = 0;     d[s] = 0;    q.push(e);//入队     while (!q.empty())//队列非空     {        e = q.top(); q.pop();//出队        int v = e.d;//取出节点编号        int d1 = e.x;        if (d[v] < d1) continue;//说明在这个点再此之后又入队了        //此次出队的并不是s到这个点的最短路,        //所以在这次更新前点v所连的点已经更过一次了         //所以后面也不会进行松弛操作         int len = a[v].size();         for(i = 0; i < len; i++)        {            haha g = a[v][i];            if ((d[v] + g.x < d[g.d]) || (d[g.d] == -233))//松弛操作             {                d[g.d] = d[v] + g.x;                e.d = g.d;                e.x = d[g.d];                q.push(e);            }        }    }    for (i=1; i <= n; i++) //输出     if (d[i] == 2000003647) cout << 2147483647 << " "; else cout << d[i] << " ";    return 0;}

版本二(前向星存图)

#include<cstdio>#include<iostream>#include<algorithm>#include<queue>using namespace std;const int INF = 2147483647;const int maxn = 10000 + 10;const int maxm = 500000 + 10;int n, m, s;int fir[maxn], nxt[maxm], to[maxm], val[maxm], cnt;void add_edge(int u, int v, int w) //前向星加边{    nxt[++cnt] = fir[u];    fir[u] = cnt;    to[cnt] = v;    val[cnt] = w;}struct Node {    int d, id;    Node(){}    Node(int d, int id) : d(d), id(id){}    bool operator < (const Node& rhs) const      {        return d > rhs.d;//重载 < 方便堆      }};int dis[maxn], vis[maxn];void Dijkstra(int s){    for(int i = 1; i <= n; i++) dis[i] = INF;    dis[s]=0;    priority_queue<Node> Q;    Q.push(Node(0,s));    while(!Q.empty())       {        Node u = Q.top(); Q.pop();        if(vis[u.id]) continue;  //若某个点已经被更新到最优,就不用再次更新其他点        vis[u.id] = 1;        for(int e = fir[u.id]; e; e = nxt[e])           {            int v = to[e], w = val[e];            if(u.d + w < dis[v])               {                dis[v] = u.d + w;                Q.push(Node(dis[v],v));              }          }      }}int main(){    scanf("%d%d%d",&n ,&m ,&s);    for(int u, v, w, i=0; i < m; i++)      {        scanf("%d%d%d",&u ,&v ,&w);        add_edge(u, v, w);      }    Dijkstra(s);    for(int i = 1; i <= n; i++) printf("%d ", dis[i]);    return 0;}

版本三(运用pair存储)

#include<iostream>#include<cstdio>#include<algorithm>#include<utility>#include<vector>#include<queue>#define MAXN 20000#define INF 2147483647using namespace std;typedef pair<int,int> pii;priority_queue<pii, vector<pii>, greater<pii> > pq;struct edge{    int to;    int cost;};vector<edge> G[MAXN];//g[i]--i to g[i].to cost costint n, m, s;int dis[MAXN];void dijk(int s){    for(int i = 1; i <= n; i++)        dis[i] = INF;    dis[s] = 0;    pq.push(make_pair(0,s));   // cout<<dis[s]<<endl;    while(!pq.empty())    {        pii u = pq.top();        pq.pop();        int x = u.second; // bian hao        //cout<<x<<endl;        for(int i = 0; i < G[x].size(); i++)        {            edge e = G[x][i];            if(dis[e.to] > dis[x] + e.cost)            {                dis[e.to] = dis[x] + e.cost;                pq.push(make_pair(dis[e.to], e.to));               // cout<<dis[e.to]<<endl;            }        }    }}int main(){    cin >> n >> m >> s;    int from, to, cost;    edge in;    for(int i = 0; i < m; i++)    {        scanf("%d%d%d",&from ,&to ,&cost);        in.to = to; in.cost = cost;        G[from].push_back(in);    }   // cout<<endl;    dijk(s);    for(int i = 1; i <= n; i++)        printf("%d ", dis[i]);    return 0;}

这里写图片描述

原创粉丝点击