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

6 篇文章 0 订阅

目录

直接调用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);
    }
Dijkstra算法是解决单源最短路径问题的一种经典算法,其基本思想是利用贪心的思想,每次选取未确定最短路径的节点中距离起点最近的节点,然后根据该节点更新与该节点相邻的节点的距离。具体实现可以采用邻接表或邻接矩阵来表示图,同时利用优先队列STL来维护节点距离的更新。 算法步骤如下: 1.初始化:将起点的距离设置为0,其余节点的距离设置为无穷大,将所有节点标记为未确定最短路径。 2.选择当前距离起点最近的未确定最短路径节点,将其标记为确定最短路径,并更新其相邻节点的距离。 3.重复步骤2直到终点被标记为确定最短路径或者所有节点均被标记为确定最短路径。 4.输出最短路径。 使用邻接表可以较为简便地实现Dijkstra算法,具体实现如下: ```c++ #include<bits/stdc++.h> using namespace std; const int MAXN=100010; const int INF=0x3f3f3f3f; struct Edge{ int to,w; }; vector<Edge> G[MAXN]; int dis[MAXN]; bool vis[MAXN]; void dijkstra(int s){ priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q; memset(dis,INF,sizeof(dis)); memset(vis,false,sizeof(vis)); dis[s]=0; q.push(make_pair(0,s)); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=true; for(int i=0;i<G[u].size();i++){ int v=G[u][i].to; int w=G[u][i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; q.push(make_pair(dis[v],v)); } } } } int main(){ int n,m,s,t; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); G[u].push_back((Edge){v,w}); G[v].push_back((Edge){u,w}); } dijkstra(s); printf("%d\n",dis[t]); return 0; } ``` 其中,邻接表G[u]表示节点u的相邻节点,Edge结构体表示边的信息,dis[u]表示起点到节点u的最短路径长度,vis[u]表示节点u是否被标记为确定最短路径。使用优先队列STL来维护节点距离的更新,pair<int,int>表示节点距离和节点编号,greater<pair<int,int>>表示节点距离的比较器,使得距离小的节点在队列前面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值