最短路的一些板子

  起因是一场训练赛,这道题(LA 5854 Long Distance Taxi)我和Thor拿出了正解(之一),但非常令人发指地送出了不计其数的TLE。

  题干不说了,我们的做法是前向星建图,然后每个LPG(起点终点也算LPG)跑一遍SPFA,然后所有LPG重新前向星建图,再跑一遍SPFA。复杂度大概是O(k1*n+k2*m^2),按理说不会超时啊……

  然后我们发现sd0061的做法前面和我们一样,后面建邻接矩阵,跑一遍Floyd,理论复杂度是O(k*n+m^3);令人发指的是竟然比我们的程序快一倍……

  于是我和Thor开始了大规模的研究。具体过程按下不表,其中重要的一点是,我们发现两次建图中第一次是稀疏图,第二次是稠密图。最终结论是:

  对于稀疏图,优先使用前向星或邻接表+SPFA(为保证速度避免使用vector);对于稠密图,优先使用邻接矩阵+Dijkstra。

  算法上,设某图点数为V,边数为E。邻接矩阵+裸Dijkstra复杂度是O(V^2),邻接表+Dijkstra+priority_queue的复杂度是O((V+E)logV),速度上是最稳定的;Barty的板子是Dijkstra+heap,但heap用set去实现,复杂度相同但实测速度上要慢上一些。前向星+SPFA+SLF+LLL的理论复杂度依旧是最快的O(k*E),但是常数k很不稳定,有退化成O(V*E)的可能;其中两个优化同时使用效果会好一些。

  所以如果图是稠密图,那么用SPFA就是作死……但是如果图中有负权边,又必须放弃Dijkstra投奔SPFA的怀抱……如果是带负权边的稠密图,提刀砍向出题人吧……

  此外从Bellman-Ford改过来的SPFA还可判负环。

  目前Damocles已有的最短路板子速度上已经比较快,基本不会再改了。贴出来供参考。

  建图:

const int MAXN=100005;
const int MAXM=200005;
struct graph
{
    int head[MAXN];
    int to[MAXM];
    int next[MAXM];
    int len[MAXM];
    int tot;
    void init()
    {
        tot=0;
        memset(head,0xff,sizeof(head));
    }
    void add(int x,int y,int z)
    {
        to[tot]=y;
        len[tot]=z;
        next[tot]=head[x];
        head[x]=tot++;
    }
} g;

  Dijkstra+priority_queue+邻接表,邻接矩阵的在此基础上随便改改就成:

int dist[MAXM];
void dijkstra(int src)
{
    memset(dist,0x3f,sizeof(dist));
    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;
        pq.pop();
        if(d!=dist[x])
            continue;
        for(int i=g.head[x]; ~i; i=g.next[i])
        {
            int y=g.to[i];
            if(d+g.len[i]<dist[y])
            {
                dist[y]=d+g.len[i];
                pq.push(make_pair(dist[y],y));
            }
        }
    }
}

  SPFA+SLF+LLL+邻接表,这个就别改邻接矩阵了,太慢:

int dist[MAXM];
bool inque[MAXM];
void spfa(int src)
{
    memset(dist,0x3f,sizeof(dist));
    memset(inque,false,sizeof(inque));
    dist[src]=0;
    deque<int> q;
    q.push_back(src);
    inque[src]=true;
    long long sum=0;
    while(!q.empty())
    {
        int x=q.front();
        q.pop_front();
        if(!q.empty()&&(long long)dist[x]*q.size()>sum)
        {
            q.push_back(x);
            continue;
        }
        sum-=dist[x];
        inque[x]=false;
        for(int i=g.head[x]; ~i; i=g.next[i])
        {
            int y=g.to[i];
            int d=dist[x]+g.len[i];
            if(d<dist[y])
            {
                if(inque[y])
                    sum+=d-dist[y];
                dist[y]=d;
                if(!inque[y])
                {
                    if(!q.empty()&&dist[q.front()]>dist[y])
                        q.push_front(y);
                    else
                        q.push_back(y);
                    sum+=dist[y];
                    inque[y]=true;
                }
            }
        }
    }
}

  基本上比赛能用上的就这两份板子了,根据情况改图或增减优化即可。通常来讲SPFA不加SLF和LLL优化也够用了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值