HDOJ2680——Dijkstra堆优化

HDOJ2680——Dijkstra堆优化

参考博客
使用邻接矩阵实现的dijkstra算法的复杂度是O(V²)。使用邻接表的话,更新最短距离只需要访问每条边一次即可,因此这部分的复杂度是O(E).但是每次要枚举所有的顶点来查找下一个使用的顶点,因此最终复杂度还是O(V²)。在|E|比较小时,大部分的时间都花在了查找下一个使用的顶点上,因此需要使用合适的数据结构进行优化。

需要优化的是数值的插入(更新)和取出最小值两个操作,因此使用堆就可以了。把每个顶点当前的最短距离用堆来维护,在更新最短距离时,把对应的元素往根的方向移动以满足堆的性质。而每次从堆中取出的最小值就是下一次要用的顶点。这样堆中的元素共有O(V)个,更新和取出的操作有O(E)次,因此整个算法的复杂度是O(ElogV)。
下面是使用STL的priority_queue实现。在每次更新时往堆里插入当前最短距离和顶点的值对。插入的次数是O(E)次,当取出的最小值不是最短距离的话,就丢弃这个值。这样整个算法也可以在同样的时间内完成。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
typedef pair<int,int> P;   //用pair存储优先队列中的数据。first中是当前最短距离,second中是定点名称
const int inf = 0x3f3f3f3f;
const int N = 1024;
vector<P> mat[N];
int n,m,d[N];

void dijkstra()
{
  priority_queue<P,vector<P>,greater<P> > pq;  //定义规则为越小越优先出的队列
  for(int i=0;i<=n;i++) d[i] = inf;
  d[0] = 0;
  pq.push(make_pair(0,0));  //把起点压入队列
  while(!pq.empty())
  {
    P p = pq.top(); //将队列顶部元素弹出
    pq.pop();
    int node = p.second;
    int len = mat[node].size();
    if(d[node] < p.first) continue;  //已经有更短的路径与他连接,那么忽略。
    for(int i=0;i<len;i++)
    {
      int tmp = mat[node][i].first;
      if(d[tmp] > d[node] + mat[node][i].second)
      {
        d[tmp] = d[node] + mat[node][i].second;
        pq.push(make_pair(d[tmp],tmp));
      }
    }
  }
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);
  int s,a,b,cost,tmp;
  while(cin >> n >> m >> s)
  {
    for(int i=0;i<=n;i++) mat[i].clear();   //初始化邻接表
    for(int i=1;i<=m;i++)
    {
      cin >> a >> b >> cost;
      mat[a].push_back(make_pair(b,cost));
    }
    cin >> tmp;
    for(int i=1;i<=tmp;i++)
    {
      cin >> a;
      mat[0].push_back(make_pair(a,0));
    }
    dijkstra();
    cout << ((d[s] == inf) ? -1 : d[s]) << endl;
  }
  return 0;
}
... ... ...