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;
}
posted @ 2019-03-08 10:43  LFYZOI题解  阅读(546)  评论(0编辑  收藏  举报