单源最短路径Dijkstra+优先队列优化 JOJ 1635. 经济出行计划 JOJ 1235. Dijkstra

本文探讨了如何使用C++标准库中的priority_queue容器和<algorithm>中的堆操作来实现Dijkstra最短路径算法,并分析了两种实现方式的特点及适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

直接调用priority_queue

JOJ 1235. Dijkstra HDU 某个题都可以通过,但是JOJ 1635. 经济出行计划只能通过90%的数据。
后来发现出错的原因是priority_queue进行push()的时候,如果有相同数据是不会进行更新heap的。同样的,如果更新数据,heap是不会进行更新的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<queue>
#include<vector>
typedef long long ll;
using namespace std;
int N,M,S,E,tot,Next[2000100],head[2000100],val[2000100],edge[2000100];
ll dis[2000100];
bool visited[2000100];
void add(int x,int y,int z){
    tot++;
    Next[tot] = head[x];
    head[x] = tot;
    edge[tot] = y;
    val[tot] = z;
}
struct cmp{
    bool operator()(int a,int b){
        return dis[a] > dis[b];
    }
};
priority_queue<int,vector<int>,cmp> Q;

int main() {
    cin>>N>>M;
    S = 1;
    E = N;
    int a,b,c;
    tot = 0;
    memset(visited,false,sizeof(bool)*2000100);
    memset(Next,0,sizeof(int)*2000100);
    memset(head,0,sizeof(int)*2000100);
    memset(val,0,sizeof(int)*2000100);
    memset(edge,0,sizeof(int)*2000100); 
    for(int i = 0;i<M;i++){
        cin>>a>>b>>c;
        if(a==b) continue;
        add(a,b,c);
        add(b,a,c);         
    }

    for(int i = 1;i<=N;i++){
        dis[i] = LLONG_MAX;
    }
    dis[S] = 0;
    Q.push(S);
    while(!Q.empty()){
        int t = Q.top();
        Q.pop();
        if(visited[t]) continue;
        visited[t] = true;
        for(int i = head[t];i;i = Next[i]){
            if(!visited[edge[i]]&&val[i]+dis[t]<dis[edge[i]]){
                dis[edge[i]] = val[i]+dis[t];
                Q.push(edge[i]);
            }
        }
    }
    cout<<dis[E]<<endl;
}

采用algorithm中的push_heap()等操作

提供的操作包含:
make_heap(),push_heap(),pop_heap(),sort_heap()
注意,这里是不提供adjust_heap()的!

sort_heap()(除了这个函数都用到了)
Sort elements of heap
Sorts the elements in the heap range [first,last) into ascending order.
The elements are compared using operator< for the first version, and comp for the second, which shall be the same as used to construct the heap.
The range loses its properties as a heap.

注意参数中的Comp是实例而不是结构(和priority_queue参数不同,这是因为pq是个类,Compare结构也未实例化)

这次的修改非常懒,更改pq中元素的时候,重新建堆。毕竟调整元素并不多(?)而且建堆时间只有3N。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<queue>
#include<vector>
typedef long long ll;
using namespace std;
int N,M,S,E,tot,Next[2000100],head[2000100],val[2000100],edge[2000100];
ll dis[2000100],inq[1000100]{};
bool visited[2000100];
void add(int x,int y,int z){
    tot++;
    Next[tot] = head[x];
    head[x] = tot;
    edge[tot] = y;
    val[tot] = z;
}
struct cmp{
    bool operator()(int a,int b){
        return dis[a] > dis[b];
    }
}comp;
vector<int>Q;
int main() {
    cin>>N>>M;
    S = 1;
    E = N;
    int a,b,c;
    tot = 0;
    memset(visited,false,sizeof(bool)*2000100);
    memset(Next,0,sizeof(int)*2000100);
    memset(head,0,sizeof(int)*2000100);
    memset(val,0,sizeof(int)*2000100);
    memset(edge,0,sizeof(int)*2000100); 
    for(int i = 0;i<M;i++){
        cin>>a>>b>>c;
        if(a==b) continue;
        add(a,b,c);
        add(b,a,c);         
    }

    for(int i = 1;i<=N;i++){
        dis[i] = LLONG_MAX;
    }
    dis[S] = 0;

    Q.push_back(S);
    push_heap(Q.begin(),Q.end(),comp);
    inq[S] = true;

    while(!Q.empty()){
        int t = Q.front();

        pop_heap(Q.begin(),Q.end(),comp);
        Q.pop_back();
        inq[t] = false;

        if(visited[t]) continue;
        visited[t] = true;
        for(int i = head[t];i;i = Next[i]){
            if(!visited[edge[i]]&&val[i]+dis[t]<dis[edge[i]]){
                dis[edge[i]] = val[i]+dis[t];
                if(inq[edge[i]]){
                    make_heap(Q.begin(),Q.end(),comp);
                }
                else{
                    Q.push_back(edge[i]);
                    push_heap(Q.begin(),Q.end(),comp);
                    inq[edge[i]] = true;                    
                }

            }
        }
    }
    cout<<dis[E]<<endl;
}

More

这是adapter设计模式的缺陷:没有办法调用底层的函数。况且heap部分也没有提供接口可以decrease元素的值,如JOJ 1216.heap需要的那样。这其实是heap一个非常好的特性,但是compare是用户自定义的,不容易检查decrease是否合法。而且这种操作对于大多数实际上用的并不多,平衡可靠性和性能后,变成了目前heap的四个接口。

PS: decrese操作接口的实现是多么的简单:

    void decrease(int i,int v){
        Node* node = get(i);
        node->v = node->v - v;
        up(node);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值