FZOJ4837
由于FZOJ很狗题面是图片所以粘了图
一看题 哇 良心送分题 ! 如果是公路就加入双向边 飞机就加入单向边
裸最短路 果断SPFASPFASPFA 结果TLE88分。。。
由于变态出题人卡SPFA 所以要用到SPFA的优化
先说一下为什么SPFASPFASPFA会被卡吧
SPFA是一个玄学算法 在稀疏图中跑得特别优秀 时间复杂度是O(KE)O(KE)O(KE),KKK是常数。看起来好像非常好的亚子…
但它的最坏情况会被卡到O(VE)O(VE)O(VE) 比如网格图就可以做到把SPFASPFASPFA卡爆 所以我们要用到一些优化
SLFSLFSLF优化
酸辣粉 SLFSLFSLF优化的操作就是将SPFASPFASPFA的队列变为双向队列。
先将源点s放入队尾,遍历出边。如果v的dis值小于队首的dis值 就把v放入队首,否则放入队尾
SLFSLFSLF优化的代码
SLFSLFSLF优化非常优秀 可以将一个4000ms的SPFASPFASPFA拉回到700ms
LLLLLLLLL优化
具体思路是假设当前从队列取出的点为uuu,如果当前点uuu到源点SSS的距离dis[u]dis[u]dis[u]小于队列QQQ中所有点的距离平均值∑dis[i]∣Q∣{\sum{dis[i]}} \over |Q|∣Q∣∑dis[i],即dis[u]∗∣Q∣<∑i∈Qdis[i]dis[u]*|Q| \lt \sum\limits_{i \in Q}{dis[i]}dis[u]∗∣Q∣<i∈Q∑dis[i](在代码实现上把这个判定式子转换成乘法判断会更简单),那么就将uuu放到队列末尾,直到找到一个点xxx比平均值要小,再从xxx开始拓展更新,这个优化感觉效果一般,似乎不如代码简洁的SLFSLFSLF,而且有时候甚至比普通的SPFASPFASPFA都慢
本题代码:
#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');
}
}
}
总结:SPFASPFASPFA非常玄学…虽然是中国人发明的算法, 但在大型考试中 不要显现出你有多爱国 得分更重要。所以能用dijkstradijkstradijkstra的堆优化(时间复杂度稳定的O(ElogE)O(ElogE)O(ElogE)尽量不要用SPFASPFASPFA…如果一定要用SPFASPFASPFA也要加上SLFSLFSLF优化…
PS:我比较菜而且不会用优快云 有事不要私信 加QQ:407694747