最短路模板

个人理解

自己以前也做了一些最短路的水题,基本上都差不多,所以这里我写一个模板,以后留着方便自己使用。当然我用模板的这道题是POJ 2378 Til the Cows Come Home。

大家可以发现,直接用邻接矩阵存的话空间开销很大,用时为O(n^2),但是最短路算法在图论中实在太重要了,因此需要优化。显然可以用邻接表优化,优化后的时间复杂度为O(logn*m),大家知道m最大可以达到n*n,这样时间复杂度不是还高了吗?是的,但是很多情况下图中的边没有那么多,通常m远小于n*n,这种图称为稀疏图,与之对立的为稠密图。这里给出用邻接表的做法。

int n,m;  //n表示图中的顶点数,m表示图中的边数
int pre[maxn]; // pre[u]保存关于结点u的第一条表的编号,
struct Edge
{
    int from,to,dist,next;  //这里的next表示从同一结点出发的下一条边的编号
}e[maxm];    

void read_graph()
{
    memset(pre,-1,sizeof(pre));  //初始化设为-1,可以直接memset
    int f,t,d;
    for(int i = 0; i < m; i++)   //边开始插入进去。
    {
        scanf("%d%d%d", &f, &t, &d);
        e[i].from = f;
        e[i].to = to;
        d[i].dist = d;
        e[i].next = pre[f];
        pre[f] = i;
    }
}

//找从u出发的所有边

for(int i = pre[u], i != -1; i = next[i].next)

通过上述代码你们会发现这里初始化的时候插入在链表的首部并非底部,避免了对链表的遍历,不过需要注意的是同一顶点的各条边在邻接表中的顺序与读入顺序正好相反。

但是这还不是最优,我们可以用优先队列优化直接找到每一次距离最短的点。这里要说明的是,我的模板并没有用邻接表,我用了vector来代替了邻接表,感觉这样更容易封装在结构体中。同时加入优先队列之后即使是稠密图时间也比直接用邻接矩阵快,因为在往优先队列中插入元素的时候,必须要d[e.to] > d[u] + e.dist,如果这个式子不成立,那么push操作将会很少。

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1005;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> pii;

struct Edge
{
    int from,to,dist;
    Edge(int f,int t,int d):from(f), to(t), dist(d){}
};

struct Dijkstra
{
    int n,m;
    vector <Edge> edges;
    vector <int> G[maxn];
    int d[maxn];
    bool done[maxn];

    void init(int n)
    {
        this->n = n;
        for(int i = 1; i <= n; i++)
            G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int dist)
    {
        edges.push_back(Edge(from,to,dist));
        edges.push_back(Edge(to,from,dist));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    void dijkstra(int s)
    {
        priority_queue <pii, vector<pii>, greater<pii> > pq;
        for(int i = 1; i <= n; i++)
            d[i] = INF;
        d[s] = 0;
        memset(done, 0, sizeof(done));
        pq.push(pii(0,s));
        while(!pq.empty())
        {
            pii pre = pq.top(); pq.pop();
            int u = pre.second;
            if(done[u]) continue;
            done[u] = true;
            for(int i = 0; i < (int)G[u].size(); i++)
            {
                Edge& e = edges[G[u][i]];
                if(d[e.to] > d[u] + e.dist){
                    d[e.to] = d[u] + e.dist;
                    pq.push(pii(d[e.to], e.to));
                }
            }
        }
    }
}graph;


int main()
{
    int T,N;
    while(scanf("%d%d", &T, &N) != EOF)
    {
        graph.init(N);
        int from,to,dist;
        for(int i = 0; i < T; i++)
        {
            scanf("%d%d%d", &from, &to, &dist);
            graph.AddEdge(from, to, dist);
        }
        graph.dijkstra(1);
        printf("%d\n", graph.d[N]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值