洛谷P3371、P4779 最短路Dijikstra优化历程(邻接矩阵->邻接表->链式前向星->堆优化)

洛谷P3371、P4779 最短路Dijikstra优化历程

两个题目链接:
洛谷P3379:基础数据:用邻接表或链式前向星即可过掉。(点数规模超过10e4=>邻接表,一般用临街矩阵的都是规模在100)
洛谷P4779:数据进行增强:需要邻接表和链式前向星优化空间+堆优化提高效率

背景:近来在洛谷刷题 (Aczy156的首页 ),碰到这个题比较麻烦,一开始用邻接矩阵直接MLE了三个点,其他全wa,后来看了题解需要邻接表。后来在学LCA的时候有学到图的新的表示方法-链式前向星(博主在个人blog中整理的 Algorithm14.0 图论 图的四种常用的存储形式顺便引引流,当然还有整理的一些其他算法的相关博客),然后又扭头回来重写了一遍,基础数据a了,但是P4779数据增强又t了。。。真是命运多舛,又接(看)着(看)学(题)习(解),发现需要用堆优化。。。


然后就过来写了个blog。

邻接矩阵 直接MLE

#include <iostream>
using namespace std;
#define maxn 10010
typedef long long ll;
ll n, m, bg, dis[maxn], vis[maxn], mp[maxn][maxn], ta, tb, tc, tem, v;
const int INF = (1<<31)-1;

void dijkstra(){
    for (int i = 1; i <= n; ++i) dis[i] = mp[bg][i];
    vis[bg] = 1;vis[bg] = 1;
    for (int i = 1; i <= n; ++i) {
        tem = INF,v = bg;
        for (int j = 1; j <= n; ++j) if (!vis[j] && dis[j] <= tem) tem = dis[v=j];
        vis[v] = 1;
        for (int j = 1; j <= n; ++j) if (!vis[j] && dis[j] > dis[v]+mp[v][j]) dis[j] = dis[v]+mp[v][j];
    }
    for (int i = 1; i <= n; ++i) printf("%lld ", dis[i]);
}

int main() {
    scanf("%lld%lld%lld", &n, &m, &bg);
    for (int i = 0; i < m; ++i) {
        scanf("%lld%lld%lld", &ta, &tb, &tc);
        mp[ta][tb] = mp[tb][ta] = tc;
    }
    dijkstra();
    return 0;
}

一开始MLE了3个,wa了7个,以为代码有问题,后来前前后后改了好几遍
在这里插入图片描述

邻接表(P3379的AC代码)

#include <iostream>
using namespace std;
#define maxn 50010
int n, m, bg, dis[maxn], vis[maxn], ta, tb, tc, tem, v;
#include <vector>
struct edge{
    int to, cost;
};
vector<edge> gh[maxn];
const int INF = 2147483647;

void dijkstra() {
    for (int i = 1; i <= n; ++i) dis[i] = INF;
    dis[bg] = 0;
    for (int i = 1; i <= n; ++i) {
        tem = INF, v = bg;
        for (int j = 1; j <= n; ++j) if (!vis[j] && dis[j] < tem) tem = dis[v=j];
        vis[v] = 1;
        for (int j = 0; j < gh[v].size(); ++j) {
            int tg = gh[v][j].to;
            if (!vis[tg] && dis[tg] > dis[v]+gh[v][j].cost) dis[tg]=dis[v]+gh[v][j].cost;
        }
    }
    for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
}

int main() {
    scanf("%d%d%d", &n, &m, &bg);
    for (int i = 0; i < m; ++i) {
        edge teme;
        scanf("%d%d%d", &ta, &teme.to, &teme.cost);
        gh[ta].push_back(teme);
    }
    dijkstra();
    return 0;
}

A掉基础数据了

堆优化(P4779的AC代码)

后来扭头回来写了链式前向星的,交了一发数据增强之后的P4779,结果全t了在这里插入图片描述
然后发现题解中需要进行堆优化来提高速度
最终通过优先队列来存放node,重载运算符让边权小的(dis)排在前面,然后每次提取堆顶,即可。(用链式前向星存储图)

#include <iostream>
using namespace std;
#define maxpoint 100010
#define maxedge 200010
#define INF 2147483647
int n, m, s, ta, tb, tc, dis[maxpoint], vis[maxpoint], v, tem=INF;
struct node {
    int to, next, cost;
}edge[maxedge]; int head[maxpoint], tot;
#include <queue>
struct nd {
    int dis, pos;
    nd(int dis, int pos): dis(dis), pos(pos) {}
    bool operator < (const nd n) const {return dis > n.dis;}
};
priority_queue<nd> pq;

void addedge(int from, int to, int cost) {
    edge[++tot].to = to;
    edge[tot].next = head[from];
    edge[tot].cost = cost;
    head[from] = tot;
}

/*
 * 堆优化
 */
void dijkstra() {
    for (int i = 1; i <= n; ++i) dis[i] = INF;
    dis[s] = 0;
    pq.push(nd(0, s));
    while (!pq.empty()) {
        nd nw = pq.top();pq.pop();
        int from = nw.pos;
        if (!vis[from]) {
            vis[from] = 1;
            for (int i = head[from]; i; i=edge[i].next) {
                int to = edge[i].to;
                if (dis[to] > dis[from]+edge[i].cost) {
                    dis[to] = dis[from]+edge[i].cost;
                    if (!vis[to]) pq.push(nd(dis[to], to));
                }
            }
        }
    }
    for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
}

/*
 * 非堆优化 
 */
//void dijkstra() {
//    for (int i = 1; i <= n; ++i) dis[i] = INF;
//    dis[s] = 0;
//    for (int i = 1; i <= n; ++i) {
//        tem = INF;v = s;
//        for (int j = 1; j <= n; ++j) if (!vis[j] && dis[j] < tem) tem = dis[v=j];
//        vis[v] = 1;
//        for (int j = head[v]; j; j=edge[j].next) {
//            int to = edge[j].to;
//            if (!vis[to] && dis[to] > dis[v]+edge[j].cost) dis[to] = dis[v]+edge[j].cost;
//        }
//    }
//    for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
//}

int main() {
    scanf("%d%d%d", &n, &m, &s);
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d%d", &ta, &tb, &tc);
        addedge(ta, tb, tc);
    }
    dijkstra();
    return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
void QueryWindow::dijkstra_heap(Lgraph Graph,int s,int stime,pre *prenum,int flag) { /*堆优化Dijikstra*/ //int day = 0; int top = 0; heap node; node.dis = stime;//将起始点即编号为s的点的到达时间设为stime node.ver = s;//起始点编号为s //初始化状态数组和最小距离数组和(驱节点和航班/列次)数组 for(int i = 0; i < Graph->Nv; i ++ ) { st[i] = false; dist[i] = INF; prenum[i].name = QString(); prenum[i].prepoint = -1; if(flag == 1 && !Graph->f_G[i].canFly) st[i] = true; } dist[s] = stime;//初始化起点的时间 push(h,top,node);//将起点入堆 while(top){ auto x = pop(h,top);//取出堆顶元素 int ver = x.ver; if(st[ver]) continue;//如果已经被选中就不再选了 st[ver] = true;//不然就选它 AdjVNode i = new adjVNode; int distance = 0; if(flag == 1){//飞机还是火车 //添加等待时间 distance = x.dis + wait_time; if(ver == s) distance -= wait_time; i = Graph->f_G[ver].First;//确定初边 }else{ distance = x.dis + wait_time2; if(ver == s) distance -= wait_time2; i = Graph->t_G[ver].First; } for( ; i ; i = i->next) { int j = i->adjv; int current_time = distance % 24;//现在的时间对应几点 int wait = (i->weight.go - current_time + 24) % 24;//如果选择这个航班/火车,需要等待的时间 if(dist[j] > distance + wait + i->weight.time) { dist[j] = distance + wait + i->weight.time; node.dis = dist[j]; node.ver = j; prenum[j].prepoint = ver; prenum[j].name = i->weight.name; push(h,top,node);//会重复入堆,还可以优化 } } } }转化成伪代码
06-02
以下是伪代码 C 版本的堆优化 Dijkstra 算法: ``` void dijkstra_heap(Lgraph Graph, int s, int stime, pre *prenum, int flag) { // 初始化状态数组和最小距离数组和(驱节点和航班/列次)数组 for (int i = 0; i < Graph->Nv; i++) { st[i] = false; dist[i] = INF; prenum[i].name = QString(); prenum[i].prepoint = -1; if (flag == 1 && !Graph->f_G[i].canFly) st[i] = true; } dist[s] = stime; // 初始化起点的时间 heap h; int top = 0; heap node; node.dis = stime; node.ver = s; push(h, top, node); // 将起点入堆 while (top) { auto x = pop(h, top); // 取出堆顶元素 int ver = x.ver; if (st[ver]) continue; // 如果已经被选中就不再选了 st[ver] = true; // 不然就选它 AdjVNode i = new adjVNode; int distance = 0; if (flag == 1) { // 飞机还是火车 // 添加等待时间 distance = x.dis + wait_time; if (ver == s) distance -= wait_time; i = Graph->f_G[ver].First; // 确定初边 } else { distance = x.dis + wait_time2; if (ver == s) distance -= wait_time2; i = Graph->t_G[ver].First; } for (; i; i = i->next) { int j = i->adjv; int current_time = distance % 24; // 现在的时间对应几点 int wait = (i->weight.go - current_time + 24) % 24; // 如果选择这个航班/火车,需要等待的时间 if (dist[j] > distance + wait + i->weight.time) { dist[j] = distance + wait + i->weight.time; node.dis = dist[j]; node.ver = j; prenum[j].prepoint = ver; prenum[j].name = i->weight.name; push(h, top, node); // 会重复入堆,还可以优化 } } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值