Dijkstra跑得慢

时间:2016-03-20 03:28:20

标签: algorithm performance graph dijkstra

Spoj有一个名为HIGHWAYS的问题,基本上是找到两个城市之间的最短路径。

我第一次解决它时,我使用了Dijkstra算法......我说得对,虽然代码有点大,所以我决定用更小的代码重做它(显然行为方式相同),但它是超时限制。

我想知道他们之间有什么区别才能让这个TLE发生。

输入如下:

n            //number of test cases
c e s e      //number of cities (from 1 to c), number of edges, start and end cities
c1 c2 w      //e lines, each with connection between c1 and c2 with weight w

以下是长码(已接受):

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

#define si(n) scanf("%d", &n)
#define INF 99999
int d[100010];

struct edge {
        int v, weight;

        edge(int a, int w) {
                v = a;
                weight = w;
        }

        bool operator < (const edge & o) const {
                return weight > o.weight;
        }

};

struct vertex {
        int value;
        vector <edge> adj;

        vertex() {
            adj.clear();
        }

        vertex(int val) {
                value = val;
                adj.clear();
        }

        void add(edge a) {
                adj.push_back(a);
        }
};

struct graph {
        vertex v[100010];

        void add_v(int val) {
                vertex a(val);
                a.adj.clear();
                v[val] = a;
        }
        void add_a(int v1, int v2, int p) {
                v[v1].add(edge(v2, p));
                v[v2].add(edge(v1, p));
        }

        void dijkstra(int n, int f) {
                for(int i = 0; i <= f; i++ ) d[i] = INF;
                priority_queue < edge > Q;
                d[n] = 0;
                int current;


                Q.push(edge(n, 0));

                while (!Q.empty()) {

                    current = Q.top().v;
                    Q.pop();
                    for (int i = 0; i < v[current].adj.size(); i++) {
                        edge a = v[current].adj[i];
                        if (d[a.v] > d[current] + a.weight) {
                            d[a.v] = d[current] + a.weight;
                            Q.push(edge(a.v, d[a.v]));
                        }
                    }
                }
        }
};

int main(){

    int cases;
    si(cases);

    int v, a, ini, fim;
    int v1, v2, w;
    while(cases--){
        si(v); si(a);
        si(ini); si(fim);

        graph g;

        for(int i = 1; i <= v; i++){
            g.add_v(i);
        }

        for(int i = 0; i < a; i++){
            si(v1); si(v2); si(w);
            g.add_a(v1, v2, w);
        }

        g.dijkstra(ini, v+1);
        int dist = d[fim];

        if(dist < 0 || dist >= INF) printf("NONE\n");
        else printf("%d\n", dist);

    }

}

这是短篇(超出时间限制):

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

struct edge{
    int v, w;
    edge(){}
    edge(int a, int b){v = a; w = b;}
};
bool operator < (edge a, edge b) {return a.w < b.w;}
const int INF = INT_MAX;
typedef vector<vector<edge> > graph;
typedef priority_queue<edge> heap;
int d[100020];

void Dijkstra(graph G, int length, int s){
    for(int i = 1; i <= length; i++) d[i] = INF;
    edge base;
    base.v = s;
    base.w = d[s] = 0;
    heap H;
    H.push(base);

    while(!H.empty()){
        int current = H.top().v;
        H.pop();
        for (int i = 0; i < G[current].size(); i++) {
            edge a = G[current][i];
            if (d[a.v] > d[current] + a.w) {
                d[a.v] = d[current] + a.w;
                H.push(edge (a.v, d[a.v]));
            }
        }
    }
}

int main(){
    int cases;
    int n, m, s, e;
    int v1, v2, w;

    scanf("%d", &cases);
    while(cases--){
        scanf("%d %d %d %d", &n, &m, &s, &e);
        graph G(n + 1);

        for(int i = 0; i < m; i++){
            scanf("%d %d %d", &v1, &v2, &w);
            G[v1].push_back(edge(v2, w));
            G[v2].push_back(edge(v1, w));
        }

        Dijkstra(G, n, s);

        if(d[e] != INF) printf("%d\n", d[e]);
        else printf("NONE\n");
    }
}

2 个答案:

答案 0 :(得分:1)

不同之处在于您如何控制优先级队列。在长版本中,您首先使用较小的重量边缘,这使您能够更早地找到最佳路径并缩短许多可能的路径:

    bool operator < (const edge & o) const {
            return weight > o.weight;
    }

在短版本中,您的行为(意外地?)反转并始终采用最大权重的边缘,这意味着您可以有效地探测所有可能的路径。

    bool operator < (edge a, edge b) {return a.w < b.w;}

更改不等式运算符,两个版本的运行速度相同。

答案 1 :(得分:0)

STL的容器很慢。如有必要,请避免使用矢量。

这是我的dij:

class graph
{
public :
    int head[N],next[M],node[M];
    int dist[M];
    int tot;
    void init()
    {
        tot = 0;
        CLR(head,-1);
    }
    void add(int x,int y,int z = 1)
    {
        node[tot] = y;
        dist[tot] = z;
        next[tot] = head[x];
        head[x] = tot++;
    }
    graph() {init();}
} g;
int dist[N]; ///the distance
///src means source. ter is optional, it means terminal
void dij(int src, graph &g, int ter=-1) 
{
    memset(dist,0x3f,sizeof(dist)); ///init d[i] as a very large value
    dist[src] = 0;
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > pq;
    pq.push(make_pair(dist[src],src));
    while(!pq.empty())
    {
        int x = pq.top().second;
        int d = pq.top().first;
        if(d != dist[x])continue;
        if(x == ter)return ;
        for(int i = g.head[x] ; ~i ; i = g.next[i])
        {
            int y = g.node[i];
            if(d+g.dist[i]<dist[y])
            {
                dist[y] = d + g.dist[i];
                pq.push(make_pair(dist[y],y));
            }
        }
    }
}