spfa magic change method

spfa magic change method

\(update\) \(on\) \(2019/8/13\)

references:

  1. Hack and metaphysics optimization method spfa

  2. Learn graph theory, shortest do you really know?

  3. ETO members solution to a problem of small series collection

Digression:

About this blog topic, I think a few (the speed of light to escape ......:

  1. Those things become spfa of reincarnation on BellmanFord

  2. Welcome to the OI graph theory efficiency supremacy

  3. RE: zero-based graph theory life

  4. Run trellis, but also jammed chrysanthemum map, such spfa you like it

  5. A scientific team excellent spfa

  6. I have no problem really optimized spfa

Into the title:

First look \ (spfa \) it, \ (spfa \) stands \ (Shortest \) \ (Path \) \ (Fastest \) \ (Mathimatics-Numerical algorithms \) , in fact, \ (Bellman \) - \ ( ford \) algorithm plus a queue optimization.

Its time complexity is not stable, the fastest can reach \ (O (1) \) , will be the slowest card into \ (Bellman \) - \ (Ford \) is \ (O (nm) \)

To sum up what you learned today \ (spfa \) various magic change:

Front: read optimization

Because cancer who is likely to be the topic card time, so we need to read the excellent (not necessarily optimized output)

Standard tags used with replication :()

inline int read()
{
    int fu=1,x=0;char o=getchar();
    while(o<'0'||o>'9'){if(o=='-')fu=-1;o=getchar();}
    while(o>='0'&&o<='9'){x=(x<<1)+(x<<3)+(o^48);o=getchar();}
    return x*fu;
}

A, spfa recording path

Since I am particularly fond of this man forward StarRam map, so the first written record only way to star friends

We use a \ (pre \) precursor array of recording nodes. When relaxation is successful, we recorded \ (pre [v] = u \) just a start dfs reverse output from the end, the final output (or call) just fine.

In fact, this method can also be used on dijkstra ......

First to labeling process:

#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
struct Edge
{
    int dis,nst,to;
}edge[200010];
int n,m,head[100010],dis[100010],vis[100010],cnt,pre[100010];
void add(int a,int b,int c)
{
    edge[++cnt].nst=head[a];
    edge[cnt].to=b;
    edge[cnt].dis=c;
    head[a]=cnt;
}
void print(int x)//找路函数
{
    if (pre[x]==-1)//找到了起始点
    {
    printf("%d",x);//输出
    return ;
    }
    print(pre[x]);
    printf("-->%d",x);//输出下一个点
    return;
}
void spfa()//标准的spfa
{
    queue<int>q;
    for(int i=1;i<=n;i++)
    {
        dis[i]=0x7fffffff;
        vis[i]=0;
        pre[i]=-1;
    }
    q.push(1);
    vis[1]=1;
    dis[1]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to;
            pre[v]=u;记录v的前驱为u
            if(dis[v]>dis[u]+edge[i].dis)
            {
                dis[v]=dis[u]+edge[i].dis;
                if(!vis[v])q.push(v),vis[v]=1;
            }
        }
    }
}
int main()
{
    int a,b,c;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    spfa();
    print(n);//找从1到n的最短路径
    printf("\n%d",dis[n]);//并输出距离
    return 0;
}

Two, spfa shortest record number

We recorded the number of shortest path from the starting point to each point with i cnt array. In order to achieve count, we only need to \ (spfa \) to make the following improvements:

  1. When the (dis [u] + e [ u] [i] dis [i] = \) \ , we make the \ (cnt [i] + = cnt [u] \)

  2. When \ (dis [i]> dis [u] + e [u] [i] \) , we make the \ (cnt [i] = cnt [u] \)

  3. If and only if \ (vis [i] == 0 \) and \ (cnt [i]! = 0 \) , we only then queued v

  4. After each point in the run queue empty note cnt array, otherwise it will duplicate records

  5. If the search to the starting point of one side is n, then the search will skip it, otherwise the last cnt [n] will be cleared directly

Please be understood in conjunction with their own code:

void spfa()
{
    queue <int> q;
    for(int i=1;i<=n;i=-~i)//这里的i=-~i就相当于i++ 
    {
        dis[i]=1e9;
        vis[i]=0;
    }
    dis[1]=0;vis[1]=1;cnt[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        if(u==n)continue;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to,w=edge[i].dis;
            if(dis[v]==dis[u]+w)
            cnt[v]+=cnt[u];
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                cnt[v]=cnt[u];
            }
            if(!vis[v]&&cnt[v])
            {
                vis[v]=1;
                q.push(v);
            }
        }
        cnt[u]=0;
    }
}

Three, spfa forfeit ring

The right to be able to handle negative side is \ (Bellman \) - \ (Ford \) The biggest advantage of the algorithm. As its optimization, \ (SPFA \) also inherited forfeit ring function. We just need a \ (cnt \) the number of records for each array point into the team and the team, if the number is greater than n, then there must be a negative ring, you can directly break out

Board Code:

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct Edge
{
    int dis,nst,to;
}edge[10010];
int n,m,head[5000],dis[5000],vis[5000],cnt,num[5000];
void add(int a,int b,int c)
{
    edge[++cnt].nst=head[a];
    edge[cnt].to=b;
    edge[cnt].dis=c;
    head[a]=cnt;
}
int read()
{
    int a=0,b=0;char o;
    while((o=getchar())!='\n'&&o!=' ')
    {
        if(o=='-')b=1;
        else if(o>'9'||o<'0')continue;
        else {a=a*10;a+=o-'0';}
    }
    if(b==0)return a;
    else return -a;
}
void spfa(int s)
{
    queue <int> q;
    for(int i=1;i<=n;i++)
    {
        dis[i]=0x7fffffff;
        vis[i]=0;
    }
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();vis[u]=0;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to;
            if(edge[i].dis+dis[u]<dis[v])
            {
                dis[v]=dis[u]+edge[i].dis;
                num[v]++;
                if(num[v]>n){printf("YE5\n");return;}
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    printf("N0\n");
}
int main()
{
    int t;
    t=read();
    for(int i=1;i<=t;i++)
    {
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            a=read();b=read();c=read();
            add(a,b,c);
            if(c>=0)add(b,a,c);
        }
        spfa(1);
        for(int i=0;i<=n;i++)
        head[i]=0,num[i]=0;
        cnt=0;
    }
    return 0;
}

Four, spfa find the longest road

When studying the differential constraint algorithm, we always encounter cases seeking the longest road. Since \ (dijkstra \) greedy nature, use it to find the longest is not the best choice, so we have to use \ (SPFA \) .

But being said, if you just judge longest road relaxation, if the topic and let you draw people's heads had a normal topic and people will construct a data card you so that you spin around \ (T \) to fly

We know, \ (SPFA \) can handle negative right side, so we could have taken the right side opposite number, and then find the shortest path, opposite the final output to the shortest number

Here is the code (Not write, find time to make up

Five, spfa find short-circuits

spfa seeking Strictly short circuit

The main idea is:

  1. If the shortest point x shortest point y can be updated, then updated with the shortest point x y shortest point, point x times y short-circuits the short-circuit point updated;

  2. If the x-point shortest path can not be updated y shortest point, then the point with x shortest update times short-circuit point y;

Core code:

//dis是最短路数组,dist是次短路数组
void spfa()
{
    queue <int> q;
    for(int i=1;i<=n;i++)
    {
        dis[i]=1e9;
        dist[i]=1e9;
        vis[i]=0;
    }
    dis[1]=0;
    vis[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to,w=edge[i].dis;
            if(ju[v])continue;
            if(dis[v]>=dis[u]+w)
            {
                dist[v]=min(dist[v],dist[u]+w);
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
            else if(dist[v]>dis[u]+w)
            {
                dist[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}

spfa seeking non-strict short-circuits

This should cooperate \ (spfa \) recording path to achieve. The general idea is to: start and side \ (SPFA \) to find the shortest picture, and then delete each traverse of the shortest side of each graph to find the shortest path corresponding to the maximum output can be taken wherein

Core code:

int spfa()
{
    queue <int> q;
    for(int i=1;i<=n;i++)
    {
        dis[i]=0x3f3f3f3f;
        vis[i]=0;
    }
    dis[1]=0;
    vis[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to;
            if(!a[u][v]&&dis[v]>dis[u]+edge[i].dis)
            {
                dis[v]=dis[u]+edge[i].dis;
                if(!flag)pre[v]=u;//只在第一遍spfa时记录路径
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    return dis[n];
}
void dfs(int u)
{
    if(pre[u]==0)return;
    a[u][pre[u]]=1;
    a[pre[u]][u]=1;
    rec[++num]=spfa();
    a[u][pre[u]]=0;
    a[pre[u]][u]=0;
    dfs(pre[u]);
}

Digression: k short circuit

As the demand k shorted use the A * algorithm, and \ (spfa \) is not what the relationship, talk about it in this blog. If you are interested, then you can the venue explanations small collection - sixth bombs third title

Sixth, the optimization and Hack for spfa

This section Reference: metaphysics queue optimization method of spfa and how to look SPFA algorithm dead this argument?

Front Description: All are optimized spfa the spfa queue priority queue as close as possible, while maintaining the priority queue to log complexity, so must be lower than the Hack complexity. Talking is, no matter what you optimize spfa metaphysical method, as long as the card you want to question people, can be spfa card. So non-negative weighted graph and try not to write spfa ......

But, after all, is spfa topic, these optimization I also talk about it

1, SLF optimization

SLF optimization is to optimize the use deque \ (the Bellman \) - (Ford \) \ , comparing each node dis enqueue and dis head of the queue, if the tail is inserted into the greater, or is inserted into the first team .

if(q.size()&&dis[q.front()]<dis[v])q.push_back(v);
else q.push_front(v);

Hack: Use a daisy chain set of methods, right on the small side chain tied together with a few side can fool the algorithm several times to enter the chrysanthemum

2, LLL optimization

For each element to be dequeued u, comparing dis [U] and the average queue dis if dis [U] bigger, then it will pop into the tail, taking the first team repeated determination element is performed until presence dis [x] is smaller than the average

while (dis[u]*cnt > sum) 
{
     q.pop();
     q.push(u);
     u = q.front();
}

Hack: connecting a huge edge weights to 1, so that on the failure LLL

3, macf optimization

In the first \ ([l, r] \ ) when visit a node, put it into the first team, or into the tail. Usually taken \ (l = 2, r = \ sqrt {v} \)

mcfx optimization principle is: most over the edge as a node departure can only be updated once a solution (it means at this point if it is out of question for people out to get you through multiple nodes, and each update will resulting in a particularly long iterative, similar to the root of chrysanthemum figure), then it will reduce the priority in the queue, as you know the topic and people use this card point to you, you should also bring it first to the most first update, it is certainly not good enough

Hack: FIG effective through the grid, but the view of the card to be daisy fly

4, SLF + fault tolerant Optimization

We define fault-tolerant values \ (Val \) , when satisfied \ (dis [now]> dis [q.front ()] + val \) is inserted from the tail, or inserted from the first team.

Set \ (W \) is the sum of edge weights, \ (Val \) is generally \ (\ sqrt {w} \ )

SLF fault tolerance can make your program does not fall into local optimal solution, similar to the simulated annealing

Hack: If the right side of the small words and did not seem any good way, so the card is the card SLF practice law, and the right side to open a large sum better than \ (10 ^ {12} \)

5.SLF + swap optimization

Whenever a queue changes, if the distance is greater than the tail head of the queue, then the switch end to end.

I also can not understand why this optimization can be faster than a regular hot SLF so much

Hack: SLF card similar to plug-induced node

6. randomized optimization (Rp and gas needs in Europe

It provides three ways:

  1. Side random order: to be read into your spfa after random disruption edge (random_shuffle function can be used)
  2. Random queue: enqueuing each node, the probability of 0.5 to the first team from the team, the probability from 0.5 to enqueue the tail.
  3. Queue stochastic optimization version: after every x number of times into the team, the queue element randomly disrupted.

I like this kind of non-UAE or forget it

Seven, spfa for Minimal Cost Flow

The minimum cost maximum flow algorithm EK

EK algorithm that spfa + augmenting paths.

Our cost side view of the right to build, and then run \ (SPFA \) . In spfa, if slack is successful, then we down a node water as much as possible, and the flow path is recorded ( \ (pre [V] = U \) ).

After the finish SPFA, down previously recorded path traced back to the source point from the sink while looking augmenting path

The total cost is the smallest (the total amount of current flow path) * (s shortest path to t), and

If you can not read the venue and network flow (F) minimum-cost flow problem (after all, my language sucks ......)

Here is the code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int cnt=1,n,m,s,t,mcost,mflow;
int head[5005],pre[5005];
int dis[5005];//dis为从S到这个点的最小总费用
int flow[5005];//flow为从S到这个点的最大可更新流量
bool vis[5005];//vis为这个点有没有被标记过(废话)
struct Edge
{
    int to,nst,dis,flow;//dis是费用,flow是流量
}edge[100005];
void add(int a,int b,int c,int d)
{
    edge[++cnt].nst=head[a];
    edge[cnt].to=b;
    edge[cnt].flow=c;//输入的时候仔细看一看
    edge[cnt].dis=d;//要是把费用和流量搞反了就凉了
    head[a]=cnt;
}
bool spfa()
{
    queue <int> q;
    memset(vis,0,sizeof(vis));
    memset(dis,inf,sizeof(dis));
    dis[s]=0;
    vis[s]=1;
    flow[s]=inf;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=edge[i].nst)
        {
            int v=edge[i].to;
            if(edge[i].flow&&dis[u]+edge[i].dis<dis[v])//如果边还有流量就尝试更新
            {
                dis[v]=edge[i].dis+dis[u];//更新最短路径
                flow[v]=min(flow[u],edge[i].flow);//到达v点的水量取决于边剩余的容量和u点的水量
                pre[v]=i;//记录路径
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return dis[t]!=inf;//如果还能跑到终点,就说明还不是最大流,还要继续跑spfa
}
void update()
{
    int x=t;
    while(x!=s)
    {
        int i=pre[x];
        edge[i].flow-=flow[t];//正向边加上流量
        edge[i^1].flow+=flow[t];//反向边减去流量
        x=edge[i^1].to;//沿着记录下的路径寻找增广路
    }
    mflow+=flow[t];//累计流量
    mcost+=flow[t]*dis[t];//累计费用
}
void EK(int s,int t)
{
    while(spfa())//当还有多余流量时
    update();
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int a,b,c,d;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        add(a,b,c,d);
        add(b,a,0,-d);//依旧不要忘记反向建边
    }
    EK(s,t);
    printf("%d %d\n",mflow,mcost);
    return 0;
}

Guess you like

Origin www.cnblogs.com/xsx-blog/p/11344654.html