zoukankan      html  css  js  c++  java
  • SPFA+SLF+LLL

    关于SLF优化

    朴素SPFA使用常规队列(FIFO)更新距离,并没有考虑优化出队顺序(dis值小的优先出队)可以在一开始就把各个点的dis值限值小,从而避免大量的松弛操作,从而提高效率。这就是SLF(Small Label First)

    实现方式很简单,常规队列替换为双端队列(deque),对于一个要加入的点u,如果dis[u]<dis[Q.front()]u就放入队首,否则放入队尾。

    此外还有LLL(Large Label first)优化,思路是对每个要出队的元素u,比较dis[u]和队列中所有dis值的平均值,如果dis[u]大,那么将它弹出放到队尾,取队首元素再重复判断,直达存在dis[u]小于平均值。发现没有,LLLSLF的目的是一样的。

    当然,可以把这两种优化组合起来。

    STL双端队列

    • 全称:“double-ended queue”,deque是双端数组,而vector是单端的
    • 在操作方式上和vector相似
    • 可以随机存取元素(用[]操作符或at()方法)
    • 头部和尾部添加或移除元素非常快,在中部插入或删除元素比较慢
    • 需要添加头文件<deque>
    //定义 
    deque<int>		dq1;                 //一个存放int的deque容器
    deque<double>	dq2;                 //一个存放float的deque容器
    deque<string>	de3;                 //一个存放string的deque容器。
    //添加移除 
    dq1.push_back(elem);				//在容器尾部添加一个元素 
    dq1.push_front(elem);				//在容器头部插入一个元素 
    dq1.pop_back();						//删除容器最后一个元素 
    dq1.pop_front();					//删除容器第一个元素
    //存取 
    cout<<dq1.at(pos);					//返回pos位置的元素,如果pos越界,抛出out_of_range异常
    cout<<dq1[pos];						//返回pos位置的元素,如果pos越界,一般情况下RE 
    dq1[pos]=1;
    cout<<dq1.front();					//返回第一个元素 
    cout<<dq1.back();					//返回最后一个元素 
    //迭代器 
    dq1.begin(); 						//返回容器中第一个元素的迭代器
    dq1.end(); 							//返回容器中最后一个元素之后的迭代器
    dq1.rbegin(); 						//返回容器中倒数第一个元素的迭代器
    dq1.rend(); 						//返回容器中倒数最后一个元素之后的迭代器
    //大小 
    dq1.size();							//返回容器中元素的个数
    dq1.empty();						//判断容器是否为空
    dq1.resize(num);					//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
    dq1.resize(num, elem); 				//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
    //带参数构造 
    deque<int>		dqx(dq1.begin(), dq1.end());	//用dq1 [beg, end)区间中的元素初始化dqx 
    deque<int>      dqy(n, elem);					//用n个elem初始化dqx 
    deque<int>		dqz(dq1);						//拷贝构造函数,用dq1初始化dqz 
    //赋值
    deque<int>		dqm, dqn;
    dqm.assign(dq1.begin(), dq1.end());				//将dq1 [beg, end)区间中的数据拷贝给dqm 
    dqn.assign(n, elem);							//将n个elem拷贝赋值给dqn 
    dqn=dqm;										//整体拷贝 
    dqn.swap(de1);									//整体交换 
    //插入
    dq1.insert(pos, elem);							//pos位置插入elem 
    dq1.insert(pos, n, elem);						//pos位置插入n个elem 
    dq1.insert(pos, beg, end);						//pos位置插入迭代器[begin, end)之间的元素 
    //删除
    dq1.clear();									//清空 
    dq1.erase(beg, end);							//删除[beg,end)区间的元素,返回下一个元素的位置 
    dq1.erase(pos);									//删除pos位置的元素,返回下一个元素的位置
    

    //SPFA+SLF
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<deque>
    using namespace std;
    const int maxn=1e4+5, maxm=5e5+5, INF=2147483647;
    int dis[maxn], v[maxn], n, m, s;
    struct edge{
    	int t, w; edge *nxt;
    	edge(int to, int len, edge *next){ t=to, w=len, nxt=next; }
    };
    edge *h[maxn];
    void add(int x, int y, int z){ h[x]=new edge(y, z, h[x]); }
    
    void SPFA(int s)
    {
        for(int i=1; i<=n; i++)   dis[i]=(i==s) ? 0 : INF;
        deque<int> Q;															//Q为双端队列 
        Q.push_back(s), v[s]=1;
        while(!Q.empty())
        {
            int x=Q.front(); Q.pop_front(); v[x]=false;							//v数组用以标记点i是否在队列中 
            for(edge *p=h[x]; p; p=p->nxt)
                if(dis[p->t]>dis[x]+p->w)
                {
                    dis[p->t]=dis[x]+p->w;
                    if(!v[p->t])
                    {
                        v[p->t]=1;
                        if(Q.empty() || dis[p->t]>dis[Q.front()])	Q.push_back(p->t);
                        else										Q.push_front(p->t);
                    }
                }
        }
    }
    
    int main()
    {
        scanf("%d%d%d", &n, &m, &s);
        for(int i=1, x, y, z; i<=m; i++)	scanf("%d%d%d", &x, &y, &z), add(x,y,z);
        SPFA(s);
        for(int i=1; i<=n; i++)				printf("%d ",dis[i]);
        return 0;
    }
    

    //SPFA+SLF+LLL
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<deque>
    using namespace std;
    const int maxn=1e4+5, maxm=5e5+5, INF=2147483647;
    int dis[maxn], v[maxn], n, m, s;
    struct edge{
    	int t, w; edge *nxt;
    	edge(int to, int len, edge *next){ t=to, w=len, nxt=next; }
    };
    edge *h[maxn];
    void add(int x, int y, int z){ h[x]=new edge(y, z, h[x]); }
    
    void SPFA(int s)
    {
    	int cnt=0, sum=0;														//cnt为队列中元素个数,sum为队列中dis值总和 
        for(int i=1; i<=n; i++)   dis[i]=(i==s) ? 0 : INF;
        deque<int> Q;															//Q为双端队列 
        Q.push_back(s), v[s]=1, cnt=1;
        while(!Q.empty())
        {
            int x=Q.front();
    		while(cnt*dis[x]>sum)												//取出来,放后去,直到找到一个比平均值小的点 
    		{
    			Q.pop_front();
    			Q.push_back(x);
    			x=Q.front();
    		}
    		Q.pop_front();
    		cnt--, sum-=dis[x], v[x]=false;										//出队后,更新队列大小及队列dis值之和  
    		for(edge *p=h[x]; p; p=p->nxt)
                if(dis[p->t]>dis[x]+p->w)
                {
                    dis[p->t]=dis[x]+p->w;
                    if(!v[p->t])
                    {
                        v[p->t]=1;
                        if(Q.empty() || dis[p->t]>dis[Q.front()])	Q.push_back(p->t);
                        else										Q.push_front(p->t);
                        cnt++, sum+=dis[p->t]; 									//入队后,更新队列大小及队列dis值之和 
                    }
                }
        }
    }
    
    int main()
    {
        scanf("%d%d%d", &n, &m, &s);
        for(int i=1, x, y, z; i<=m; i++)	scanf("%d%d%d", &x, &y, &z), add(x,y,z);
        SPFA(s);
        for(int i=1; i<=n; i++)				printf("%d ",dis[i]);
        return 0;
    }
    
  • 相关阅读:
    IOS开发mapkit学习笔记
    IOS的两种框架设计思想
    关于创建及使用多线程的几种方法
    Linux基础入门 之挑战:历史命令
    20172018网络攻防第三周
    20172018网络攻防第二周
    Linux基础入门实验楼挑战之数据提取
    iconfont字体图标的使用方法超简单!
    CSS中的绝对定位(absolute)误区
    获取对象属性的点方法和中括号法的区别
  • 原文地址:https://www.cnblogs.com/lfyzoi/p/10494392.html
Copyright © 2011-2022 走看看