算法日记18:SC70最短路径(Dijkstra+优先队列priority_queue优化)

一、题目:

在这里插入图片描述

二、题解:

1、通过数据我们发现,朴素的 0 0 0(n2)Dijkstra无法解决问题,因此我们可以采用优先队列来进行优化

在这里插入图片描述

2、我们发现朴素Dijk算法中,寻找最小点采用了0(n)

 	  ll temp=-1;
      for(int j=1;j<=n;j++)   //遍历找哪个点最近
      {
          //先让temp先变成没有访问过的值
          //并且再找最近点
          if(!vis[j] &&((temp==-1)||(dis[j]<dis[temp])))  
          {
              temp=j; //更新最小值
          }
      }

这是非常没有必要的,因此我们可以想到维护一个小根堆每次从堆中取出最小值,来简化时间复杂度.

2.1 重写priority_queue()比较函数

  • p r i pri pri中,我们规定先比较两个点的权重
  • 权重相同时,再比较点数下标大小
  • 注意 p r i pri pri是使用<来维护一个大根堆,因此要写成小根堆时要重写<为大于号
struct node{
  ll v;  //指向边
  ll w;  //权重
  bool operator <(const node& u)const //重写 < ,让优先队列变成小根堆
  {
    return w==u.w ? v<u.v : w>u.w; 
  }
};

2.2、其他思路与朴素基本一致,详细代码如下:

void dijkstra(ll x)
{
    //初始化
    memset(dis,0x3f,sizeof(dis));
    dis[x]=0;
  
    priority_queue<node>pq;
    pq.push({x,dis[x]});    //将起点作为拓展点

    while(pq.size())    //表示还有点等待拓展
    {
        int temp=pq.top().v;pq.pop();   //取出队头的点

        if(vis[temp]) continue; //已经拓展过,continue
        vis[temp]=true;    //否则就拓展它

        for(auto t:g[temp])    //松弛拓展点的边
        {
            int y=t.v;  //取出边的终点和权重
            int w=t.w;

            //如果vis[y]说明y已经是最短距离,无需更新
            if(!vis[y] && dis[y]>dis[temp]+w)
            {
                dis[y]=dis[temp]+w;
                pq.push({y,dis[y]});
            }
        }
    }
}

完整代码如下:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2e5+7;
struct node{
  ll v;  //指向边
  ll w;  //权重
  bool operator <(const node& u)const //重写 < ,让优先队列变成小根堆
  {
    return w==u.w ? v<u.v : w>u.w; 
  }
};

ll n,m;
vector<node>g[N];
bool vis[N];
ll dis[N]; //距离数组


void dijkstra(ll x)
{
    //初始化
    memset(dis,0x3f,sizeof(dis));
    dis[x]=0;
  
    priority_queue<node>pq;
    pq.push({x,dis[x]});    //将起点作为拓展点

    while(pq.size())    //表示还有点等待拓展
    {
        int temp=pq.top().v;pq.pop();   //取出队头的点

        if(vis[temp]) continue; //已经拓展过,continue
        vis[temp]=true;    //否则就拓展它

        for(auto t:g[temp])    //松弛拓展点的边
        {
            int y=t.v;  //取出边的终点和权重
            int w=t.w;

            //如果vis[y]说明y已经是最短距离,无需更新
            if(!vis[y] && dis[y]>dis[temp]+w)
            {
                dis[y]=dis[temp]+w;
                pq.push({y,dis[y]});
            }
        }
    }
}


  int main()
  {
      ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
      cin>>n>>m;

      for(int i=1;i<=m;i++)
      {
          int ui,vi,wi;cin>>ui>>vi>>wi;
          g[ui].push_back({vi,wi});
      }
      dijkstra(1);
      
      if (dis[n] >= 0x3f3f3f3f) {
       cout << -1 << '\n';  // 无法到达
   } else {
       cout << dis[n] << '\n';  // 输出到n的最短距离
   }
      return 0;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值