SPFA的各种优化

文章目录

资料

  1. 知乎 - 如何看待 SPFA 算法已死这种说法?
  2. SPFA的两个优化 By 扩展的灰
  3. 知乎 - 如何卡spfa?

代码

/********************
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值