资料
代码
/********************
User:Mandy.H.Y
Language:c++
Problem:luogu4779
Algorithm:SPFA+(SLF+swap)+(阈值)
********************/
/*
作者:知乎用户
链接:https://www.zhihu.com/question/292283275/answer/484871888
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
LLL 优化:每次将入队结点距离和队内距离平均值比较,如果更大则插入至队尾。
Hack:向 1 连接一条权值巨大的边,这样 LLL 就失效了。
SLF 优化:每次将入队结点距离和队首比较,如果更大则插入至队尾。
Hack:使用链套菊花的方法,在链上用几个并列在一起的小边权边就能欺骗算法多次进入菊花。
SLF 带容错:每次将入队结点距离和队首比较,如果比队首大超过一定值则插入至队尾。
Hack:如果边权之和很小的话似乎没有什么很好的办法,因为令边权之和为 ,那么令容错值为 ,总复杂度似乎接近 。我不确定这个复杂度对不对,但是 SPFA 确实在边权和小的时候跑得蛮不错的。所以卡法是卡 SLF 的做法,并开大边权,总和最好超过 。
mcfx 优化(thanks to @mcfx and @yfzcsc):在第 次访问一个结点时,将其放入队首,否则放入队尾。通常取 。
Hack:网格图表现优秀,但是菊花图表现很差。
P.S. 此优化与 SLF 带容错一起使用有更好的效果,可以使所需要的边权上升许多。
@raffica's NTR:详见 如何卡spfa? - raffica的回答 - 知乎 。
Hack:菊花图表现很差。
SLF + swap:每当队列改变时,如果队首距离大于队尾,则交换首尾。
这个 SLF 看起来很弱,但却通过了所有 Hack 数据。
而且,非常难卡。还请各位认为 SPFA 必死的爷爷们,来亲手杀死这个苟延残喘的 SLF 吧。
UPD: Hacked by @negiizhao and @钟子谦。
Hack: 与卡 SLF 类似,外挂诱导节点即可。(是我菜了)
*/
/*
最近看到知乎上面有人讨论怎么卡spfa并给出了这道题作为实战地点
于是我就搞了两个优化把出题人精心构造的数据过了
优化1:
这个优化不是我想出来的,如果没错应该是Menci的做法
本来这个优化已经可以过掉原来的五个数据的,让后fstqwq(出题人)搞了一个新数据把这个优化卡T了
其实就是每次入队,如果队头比队尾大那么就交换(感觉很像SLF但是比SLF强)
让后为了Ac我就加了第二个优化
优化2:(原创)
首先设置一个阈值t,初始化为1
我们对每个节点x记录x所执行过的松弛操作的次数
每次从队头取出元素x时,若那么把x丢入队尾,同时增大阈值t,但不执行任何其他操作
加上这个优化就可以轻松AC
这里有一份实现代码,很简洁(但是仍然没有dijk简洁)
---------------------
作者:扩展的灰
来源:优快云
原文:https://blog.youkuaiyun.com/JacaJava/article/details/82528037
版权声明:本文为博主原创文章,转载请附上博文链接!
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=2e5+5;
int n,m,s,t,size;
int first[maxn],q[maxn],dis[maxn],cnt[maxn];
bool vis[maxn];
struct Edge
{
int v,w,nt;
}edge[maxm];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar((x%10)|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("4779.txt","r",stdin);
// freopen("4779out.txt","w",stdout);
}
void eadd(int u,int v,int w)
{
edge[++size].v=v;
edge[size].w=w;
edge[size].nt=first[u];
first[u]=size;
}
void readdata()
{
read(n);read(m);read(s);
for(int i=1;i<=m;++i)
{
int u,v,w;
read(u);read(v);read(w);
eadd(u,v,w);
}
}
void work()
{
// int times=clock();
int l,r;
t=1;l=r=0;
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[s]=0;q[r++]=s;
// printf("init时间:%.3lf\n",double(clock()-times)/CLOCKS_PER_SEC);
// times=clock();
while(l!=r)
{
// t=1;
int u=q[l++];
if(l>n) l=0;
if(cnt[u]>t)
{
q[r++]=u;
if(r>n) r=0;
t+=20;//t为阈值
continue;
}
vis[u]=0;
for(int i=first[u];i;i=edge[i].nt)
{
int v=edge[i].v,w=edge[i].w,d=dis[u]+w;
if(d<dis[v])
{
dis[v]=d;
if(!vis[v])
{
q[r++]=v;++cnt[u];//记录u的松弛次数
if(r>n) r=0;
vis[v]=1;
}
int rr;
if(r-1<0) rr=n;
else rr=r-1;//r-1可能为负
if(dis[q[l]]>dis[q[rr]]) swap(q[l],q[rr]);//SLF
}
}
}
for(int i=1;i<=n;++i)
{
put(dis[i]);
putchar(' ');
}
// printf("SPFA时间:%.3lf\n",double(clock()-times)/CLOCKS_PER_SEC);
}
int main()
{
// docu();
readdata();
work();
return 0;
}