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|
∣Q∣∑dis[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∣<i∈Q∑dis[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