[Usaco]2011 Jan Roads and Planes 道路和航线 SPFA的优化

FZOJ4837
由于FZOJ很狗题面是图片所以粘了图
在这里插入图片描述
一看题 哇 良心送分题 ! 如果是公路就加入双向边 飞机就加入单向边
裸最短路 果断 S P F A SPFA SPFA 结果TLE88分。。。
由于变态出题人卡SPFA 所以要用到SPFA的优化
先说一下为什么 S P F A SPFA SPFA会被卡吧
SPFA是一个玄学算法 在稀疏图中跑得特别优秀 时间复杂度是 O ( K E ) O(KE) O(KE), K K K是常数。看起来好像非常好的亚子…
但它的最坏情况会被卡到 O ( V E ) O(VE) O(VE) 比如网格图就可以做到把 S P F A SPFA SPFA卡爆 所以我们要用到一些优化

S L F SLF SLF优化
酸辣粉 S L F SLF SLF优化的操作就是将 S P F A SPFA SPFA的队列变为双向队列。
先将源点s放入队尾,遍历出边。如果v的dis值小于队首的dis值 就把v放入队首,否则放入队尾
S L F SLF SLF优化的代码
在这里插入图片描述
S L F SLF SLF优化非常优秀 可以将一个4000ms的 S P F A SPFA SPFA拉回到700ms
在这里插入图片描述
在这里插入图片描述
L L L LLL LLL优化
具体思路是假设当前从队列取出的点为 u u u,如果当前点 u u u到源点 S S S的距离 d i s [ u ] dis[u] dis[u]小于队列 Q Q Q中所有点的距离平均值 ∑ d i s [ i ] ∣ Q ∣ {\sum{dis[i]}} \over |Q| Qdis[i],即 d i s [ u ] ∗ ∣ Q ∣ < ∑ i ∈ Q d i s [ i ] dis[u]*|Q| \lt \sum\limits_{i \in Q}{dis[i]} dis[u]Q<iQdis[i](在代码实现上把这个判定式子转换成乘法判断会更简单),那么就将 u u u放到队列末尾,直到找到一个点 x x x比平均值要小,再从 x x x开始拓展更新,这个优化感觉效果一般,似乎不如代码简洁的 S L F SLF SLF,而且有时候甚至比普通的 S P F A ​ SPFA​ SPFA都慢

本题代码:

#include<cstdio>
#include<queue>
#include<iomanip>
#include<deque>
using namespace std;
#define INF 2000000000
inline char nc()
{ 
    static char buf[100000],*p1=buf,*p2=buf; 
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 
}//别问我为什么用fread (被卡怕了
inline void read(int &x)
{
	int s=0,w=1;char ch=nc();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=nc();}
	while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=nc();}
	x=s*w;
}//快速读入优化
void print(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>=10)print(x/10);
    putchar(x%10+'0');
}//快速输出优化
int n,r,p,s,x,y,z,cnt,head[25001],dis[25001];
bool vis[25001];
deque<int> q;
struct node
{
	int to,nxt,val;
}edge[150001];//链式前向星存图
inline void addedge(int u, int v, int w)
{
	edge[++cnt].to=v;
	edge[cnt].val=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}//航路存单向边
inline void superadd(int u, int v, int w)
{
	addedge(u,v,w);
	addedge(v,u,w);
}//公路存双向边
void Shortest_Path_Faster_Algorithm(int s)
{//SLF优化的SPFA
	q.push_back(s);//先将源点放入队尾
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		vis[u]=false;
		for(int i=head[u];i;i=edge[i].nxt)
		{
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].val)
			{
				dis[v]=dis[u]+edge[i].val;
				if(!vis[v])
				{
					vis[v]=true;
					if(q.empty()||dis[v]>dis[q.front()])q.push_back(v);
					else q.push_front(v);//如果v的dis值小于队尾就将v放入队首
				}
			}
		}
	}
}
int main()
{
	read(n);read(r);read(p);read(s);
	for(int i=1;i<=r;i++)
	{
		read(x);read(y);read(z);
		superadd(x,y,z);
	}
	for(int i=1;i<=p;i++)
	{
		read(x);read(y);read(z);
		addedge(x,y,z);
	}
	for(int i=1;i<=n;i++)dis[i]=INF;
	dis[s]=0;
	vis[s]=true;
	Shortest_Path_Faster_Algorithm(s);
	for(int i=1;i<=n;i++)
	{
		if(dis[i]==INF)puts("NO PATH");
		else 
		{
			print(dis[i]);
			putchar('\n');
		}
	}
}

总结: S P F A SPFA SPFA非常玄学…虽然是中国人发明的算法, 但在大型考试中 不要显现出你有多爱国 得分更重要。所以能用 d i j k s t r a dijkstra dijkstra的堆优化(时间复杂度稳定的 O ( E l o g E ) O(ElogE) O(ElogE)尽量不要用 S P F A SPFA SPFA…如果一定要用 S P F A SPFA SPFA也要加上 S L F SLF SLF优化…

PS:我比较菜而且不会用CSDN 有事不要私信 加QQ:407694747

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值