SPFA(Shortest Path Faster Algorithm)

Special Note
This article is reproduced from three gold (frinemore) of the blog: point this

Foreword

1. About SPFA, it did not die.

2. Next, all of the code, are their own handwriting (not checked for correctness. Replenish code has been checked, right), pointed out that there is an error please help.

SPFA principle and correctness

If given a negative view of the right side, similar to Dijkstra's algorithm algorithms will be no useless, he sent in handy on SPFA algorithm. Brevity, we agreed weighted directed graph G is absent negative cycles, i.e. the shortest path must exist. With an array of records for each node d shortest path estimation value, and with the adjacent table storing G. FIG. Our approach is dynamic approximation: the establishment of a FIFO queue to be optimized to save nodes, each node u remove the head of the queue optimization, and with u point of the current estimate of the shortest path to leave the point u points to the node v for relaxation operation , if the point v shortest path estimation value be adjusted, and the point v is not currently in the queue, the point will be placed in the tail v. Such continuously removed from the queue node relaxation operation is performed until the queue is empty.

Theorem: As long as the shortest path exist, the above-described algorithm will be able to find a minimum SPFA. Proof: each time point into the tail, after relaxation operation is achieved. In other words, each time there will be the shortest path to optimize a point estimate of v D [v] becomes smaller. Therefore, the implementation of the algorithm will make d smaller and smaller. Since we assume negative cycles is not present in FIG., Each node has the shortest path value. Thus, the algorithm is not executed infinitely continues, gradually decreases as the d value, until the value of the shortest path to the end of the algorithm, then the shortest path is the shortest path estimation value corresponding to the value of the node.

In fact, it can be understood:

Given the absence of a negative cycles of the drawing, there are n nodes, m edges

So when seeking a certain point (point 1 is assumed here) single-source shortest path, the distance to the farthest point of the n-1 most experienced side.

Then the relaxation times for n-1 m with edges of the shortest path can be determined.

Here is the Bellman - Ford Algorithm:

const int MAXNC = 10000;

struct edge {
    int x, y, val;
}a[MAXNC];

for(int i = 1; i < n; i++)
    for(int j = 1; j <= m; j++) {
        int x = a[j].x, y = a[j].y;
        if(d[y] > d[x] + a[j].val) d[y] = d[x] + a[j].val;
    } 

Forfeit ring:

bool have_negetive_circle = false;

for(int i = 1; i<= m; i++) {
    int x =a[i].x, y = a[i].y;
    if(d[y] > d[x] + a[i].val) {
        have_negetive_circle = true;
        break;
    }
}

if(have_negetive_circle) printf("...");

optimization

Since the conventional calculation complexity is very high (o (n * m)), so you have optimized look.

Wide search optimization

Since the value of d is only updated node to other nodes update the value of d, we can optimize the use queue (queue).

This is the code:

void SPFA(int s) {
    queue<int> Q;
    for(int i = 1; i <= n; i++) d[i] = INF;
    d[s] = 0;
    Q.push(s);
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = false;
        for(int i = 1; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                if(!inq[y]) Q.push(y);
            }
        }
    } 
}

However, 20 lines of code.

SPFA queue forfeit ring

Only you need to record the number of enqueued like, if greater than n, there is described a negative ring.

code:

bool SPFA(int s) {
    queue<int> Q;
    for(int i = 1; i <= n; i++) d[i] = INF, cnt[i] = 1;
    d[s] = 0; cnt[s] = 1;
    Q.push(s);
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = false;
        for(int i = 1; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                if(!inq[y]) {
                    Q.push(y);
                    inq[y] = true;
                    if(++cnt[y] > n) return true;
                }   
            }
        }
    } 
    return false;
}

In fact, on the change in the original code a little, but it has a drawback, is that the only negative ring to find a starting point from the source s, if s can not reach negative loop, then this is not checked out.

The problem, of course there is a solution, below is the code.

code:

bool have_negative_circle() {
    queue<int> Q;
    for(int i = 1; i <= n; i++) d[i] = 0, cnt[i] = 1, inq[i] = true, Q.push(i);
                                //d[i] = 0,这样更容易找负环,效率更高
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        for(int i = head[x]; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                if(!inq[y]) {
                    inq[y]  =true;
                    Q.push(y);
                    if(++cnt[y] >= n) return true;
                }
            }
        }
    }
    return false;
}

This code efficiency than the above is much higher, but can only find negative ring.

Deep Search Optimization:

I just know this optimization, but the specific is not clear, but Ben has a great God who carefully studied next to me, let him speaking.

The code slightly.

application

Here I know SPFA applications.

For the most short-circuit

If there are no negative side right, then please use more efficient Dijkstra, SPFA when dealing with extreme data will be degraded.

code:

void SPFA(int s) {
    queue<int> Q;
    for(int i = 1; i <= n; i++) d[i] = INF;
    d[s] = 0;
    Q.push(s);
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = false;
        for(int i = 1; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                if(!inq[y]) Q.push(y);
            }
        }
    } 
}

Seeking the longest road

Although most of the acyclic graph can be converted into DAG classic model solution, but SPFA also possible, dijkstra seems row (this is not clear).

Code examples speak later.

Forfeit ring

This seems to be a sign of SPFA useful to the differential constraints.

example

  1. UVA437 Tower of Babel The Tower of Babylon (difficult)

Although this problem is dynamic programming examples, but can also be the longest way to do it.

This is the meaning of the title:

There are n (n <= 30) kinds of cubes, each with an infinite number. Cube built by piling a required number of selected column as high as possible (can choose the length and breadth), such that the bottom surface of each cube are strictly less than the length and breadth of the bottom surface of the cube below its length and width.

Ideas:

Each cube can be said to be infinite times, in fact, can only use a maximum of three times. Every time when the input of a cube is then converted to the three cubes,

O (n ^ 2) building side, if less than the length and width of the bottom surface of the cube cube x y is incremented one aspect bottom surface y-> x side, the right value of the height x of the cube.

This is not enough, then we have an extra node is the high cube for each cube plus side, the right, then ran up the road just fine.

The sample code :( never before, so take a look at the principle)

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

const int MAXNC = 100, MAXN = 1000;
const long long INF = 1e11 + 9;

struct point {
    int x, y, z;
    point() {}
    point(int x, int y, int z) {
        this->x = x;
        this->y = y;
        this->z = z;
    }
}a[MAXNC];

struct edge{
    int y, next, val;
    edge() {}
    edge(int y, int next, int val) {
        this->y = y;
        this->next = next;
        this->val = val;
    }
}e[MAXN];

int n, kase, ans, cnt;
int head[MAXNC];
long long
 d[MAXNC];
bool inq[MAXNC];


bool judge(point x, point y) {
    if(x.x > y.x && x.y > y.y) return true;
    else if(x.y > y.x && x.x > y.y) return true;
    else return false;
}

void add_edge(int x,int y,int val) {
    e[++cnt] = edge(y, head[x], val);
    head[x] = cnt;
    return;
}

void SPFA(int s) {
    queue<int> Q;
    for(int i = 1; i <= 3 * n; i++) d[i] = 0;
//  d[s] = 0;
    Q.push(s);
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = false;
        for(int i = head[x]; i; i = e[i].next) {
//          printf("%d %d %d\n", x, e[i].y, e[i].val);
            int y = e[i].y;
            if(d[y] < d[x] + e[i].val) {
                d[y] = d[x] + e[i].val;
                if(!inq[y]) Q.push(y), inq[y] = true;
            }
        }
    }
}

int main() {
    scanf("%d",&n);
    while(n) {
        for(int i = 1; i < 3 *n; i += 3) {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            a[i]     = point(x, y, z);
            a[i + 1] = point(y, z, x);
            a[i + 2] = point(z, x, y);
        } 
        memset(head,0,sizeof(head));
        cnt = 0;
        
        for(int i = 1; i<= 3 * n; i++)
            for(int j = 1; j <= 3 *n; j++)
                if(i != j && judge(a[i], a[j]))
                    add_edge(i, j, a[j].z);
                    
        for(int i = 1; i <= 3 * n; i++)
            add_edge(3 * n + 1, i, a[i].z);
            
        SPFA(3 * n + 1);
        ans = 0;
        
        for(int i = 1; i<= 3 * n; i++) if(d[i] > ans) ans = d[i];
         
//      for(int i = 1; i <= 3 * n; i++) printf("%d ",-d[i]);
        
//      for(int i = 1; i <= 3 * n + 1; i++)
//          for(int j = head[i]; j; j = e[j].next) 
//          printf("x=%d y=%d val=%d\n", i, e[j].y, e[j].val);
    
        printf("Case %d: maximum height = %d\n", ++kase, ans);
        scanf("%d",&n);
    }  
    return 0;
}

Here is the code AC (problem solution longest road):

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>

using namespace std;
int n, mx;
int dis[100], visited[100];
queue<int> q;
struct cubes{
    int x;
    int y;
    int z;
}cube[100];
struct Node{
    int from;
    int to;
    int weight;
};
vector<Node> edges[100];

void spfa(){
    while(!q.empty()){
        int top=q.front(); q.pop();
        visited[top]=false;
        for(int i=0;i<edges[top].size();i++){
            int t=edges[top][i].to;
            if(dis[t]<edges[top][i].weight+dis[top]){
                dis[t]=edges[top][i].weight+dis[top];
                if(!visited[t]){
                    visited[t]=true;
                    q.push(t);
                }
            }
        }
    }
}

int main(){
    cin >> n;
    int c=0;
    while(n!=0){
        c++;
        memset(cube, 0, sizeof(cube));
        memset(dis, 0, sizeof(dis));
        for(int i=0;i<100;i++){
            edges[i].clear();
        }
        for(int i=1;i<=3*n;i+=3){
            int x, y, z;
            cin >> x >> y >> z;
            cube[i].x=x; cube[i].y=y; cube[i].z=z;
            cube[i+1].x=y; cube[i+1].y=z; cube[i+1].z=x;
            cube[i+2].x=z; cube[i+2].y=x; cube[i+2].z=y;
        }
        for(int i=1;i<=n*3;i++){
            for(int j=1;j<=n*3;j++){
                if((cube[i].x<cube[j].x && cube[i].y<cube[j].y) || (cube[i].x<cube[j].y && cube[i].y<cube[j].x)){
//                  cout << j << " to " << i << ": " << cube[j].x << " " << cube[i].x << "   " << cube[j].y << " " << cube[i].y << endl;
                    Node a;
                    a.from=j;
                    a.to=i;
                    a.weight=cube[i].z;
                    edges[j].push_back(a);
                }
            }
        }
        for(int i=1;i<=3*n;i++){
            dis[i]=cube[i].z;
            q.push(i);
            visited[i]=true;
        }
        spfa();
        int mx=0;
        for(int i=1;i<=n*3;i++){
            if(dis[i]>mx) mx=dis[i];
        }
        cout << "Case " << c << ": maximum hight = ";
        cout << mx << endl;
        cin >> n;
    }
    return 0;
}

I have not written a good-looking

2. P3385 [template] negative ring

Say this is a template, here is the code:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int MAXNC=2000+100,MAXN=500000,INF=0x7fffffff;

int d[MAXNC],head[MAXNC],num[MAXNC];
bool inq[MAXNC];
int n,m,cnt;

struct edge {
    int y,next,val;
    edge() {}
    edge(int y,int next,int val) {
        this->y=y;
        this->next=next;
        this->val=val;
    }
}a[MAXN];


void add_edge (int x,int y,int val);

bool SPFA(int s) {
    queue<int> Q;
    for(int i = 1;i <= n;i++) d[i] = INF,num[i]=0,inq[i]=0;
    Q.push(s);
    d[s] = 0; inq[s] = 1;
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = 0;
        if(num[x] >= n) return true;
        for(int i = head[x]; i; i = a[i].next) {
            int y=a[i].y;
            if(d[y]> d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                if(!inq[y]) {
                    Q.push(y);
                    inq[y]=1;
                    if(++num[y]>=n) return true;
                }
            }
        }
    }
    return false;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d", &n, &m);
        cnt=0;
        memset(head,0,sizeof(head));
        for(int i = 1;i <= m; i++) {
            int x,y,val;
           scanf("%d %d %d", &x, &y, &val);
            if(val<0) add_edge(x,y,val);
            else add_edge(x,y,val),add_edge(y,x,val);
        }   
       if(SPFA(1)) printf("YE5\n");
       else printf("N0\n");
    }   
    return 0;
}


void add_edge(int x,int y,int val){
    a[++cnt] = edge(y, head[x], val);
    head[x] = cnt;
    return ;
}

Please look at the "YE5" and "N0"

Shortest Path Dijkstra example, most can do, and more efficient, which no longer say the title of the shortest.

to sum up

SPFA shortest seek time complexity is o (KE), k is a constant, typically 2, but "degraded" special data processing. (Dijkstra does not);

SPFA may process negative-side and right ring can find negative (not the Dijkstra);

Principle SPFA algorithm (progressive method) making it a wider range of action (the BFS);

And the amount of code a lot less than Dijkstra.

A very good algorithm.

supplement

Record the path to the star before the chain

I did not know what to say, is again seeking the shortest time to record the current slack side, this side must be on the shortest path, and then little by little touched up just fine starting point.

SPFA version:

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int MAXNC = 1e5 + 100, MAXN = 1e6, INF = 0x3f3f3f3f;

struct edge {
    int x, y, next, val, cnt;  // 为了记录路径竟然多塞了这么多东西
    edge() {}
    edge(int x, int y, int next, int val, int cnt) {
        this->x = x;
        this->y = y;
        this->next = next;
        this->val = val;
        this->cnt = cnt;
    }
}a[MAXN];

vector<int> Path[MAXNC];
int head[MAXNC], d[MAXNC], p[MAXNC],inq[MAXNC];
int n, m, cnt, S;

void read();
void add_edge(int x, int y, int val);
void SPFA(int s);
void Get_Shortest_Paths(int s, vector<int>* Path);
void ask_print();

int main() {

    scanf("%d %d %d", &n, &m, &S);

    read();

    Get_Shortest_Paths(S, Path); //真是毒瘤

    ask_print();

    return 0;
}

void read() {
    for(int i = 1; i <= m; i++) {
        int x, y, val;
        scanf("%d %d %d", &x, &y, &val);
        add_edge(x, y, val);
    }
    return;
}

void add_edge(int x, int y, int val) {
    a[++cnt] = edge(x, y, head[x], val, cnt);
    head[x] = cnt;
    return;
}

void SPFA(int s) {
    queue<int> Q;
    for(int i = 1; i <= n; i++) d[i] = INF;
    d[s] = 0;
    Q.push(s);
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        inq[x] = false;
        for(int i = head[x]; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                p[y] = a[i].cnt;
                if(!inq[y] && (inq[y] = true)) Q.push(y); 
            }
        }
    }
    return;
}

void Get_Shortest_Paths(int s, vector<int>* Path) {
    SPFA(s);
    for(int i = 1; i <= n; i++) {
        Path[i].clear();
        int t = i;
        Path[i].push_back(t);
        while(s != t) {
            Path[i].push_back(a[p[t]].x);
            t = a[p[t]].x;
        }
        reverse(Path[i].begin(),Path[i].end());
    }
    return;
}


void ask_print() {
    int x;
    scanf("%d", &x);
    
    for(vector<int> :: iterator it = Path[x].begin(); it != Path[x].end(); it++) 
            printf("%d ", *it);
//  printf("\nd[%d] = %d",x,d[x]);
    printf("\n\n");
    return;  
}

/*
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
4
*/

Dijkstra version:

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int MAXNC = 1e6, MAXN = 1e4, INF = 0x3f3f3f3f;

struct heapnode {
    int x, s;
    heapnode() {}
    heapnode(int x,int s) {
        this->x = x;
        this->s = s;
    }
    bool operator < (const heapnode &rhs) const {
        return s > rhs.s;
    } 
};


struct edge{
    int x, y, next, val, cnt;
    edge() {}
    edge(int x, int y, int next, int val, int cnt) {
        this->x = x;
        this->y = y;
        this->next = next;
        this->val = val;
        this->cnt = cnt;
    }
}a[MAXNC];

vector<int> Path[MAXN];
int d[MAXN], vis[MAXN], head[MAXN], p[MAXN];
int n, m, cnt, S;

void read();
void add_edge(int x, int y, int val);
void Dijkstra(int s);
void Get_shortest_Path(int s);
void ask_print();

int main() {
    scanf("%d %d %d", &n, &m, &S);
    
    read();
    
    Get_shortest_Path(S);
    
    ask_print();
    
    return 0;
}

void read() {
    for(int i = 1; i <= m; i++) {
        int x, y, val;
        scanf("%d %d %d", &x, &y, &val);
        add_edge(x, y, val);
    }
}

void add_edge(int x, int y, int val) {
    a[++cnt] = edge(x, y, head[x], val, cnt);
    head[x] = cnt;
}

void Get_shortest_Path(int s) {
    Dijkstra(s);
    for(int i = 1; i <= n; i++) {
        Path[i].clear();
        int t = i;
        Path[i].push_back(i);
        while(t != s) {
            Path[i].push_back(a[p[t]].x);
            t = a[p[t]].x;
        }
        reverse(Path[i].begin(), Path[i].end());
    }
    return;
}

void Dijkstra(int s) {
    priority_queue<heapnode> Q;
    for(int i = 1; i <= n; i++) d[i] = INF;
    d[s] = 0;
    Q.push(heapnode(s,0));
    while(!Q.empty()) {
        heapnode tmp = Q.top(); Q.pop();
        int x = tmp.x;
        if(vis[x]) continue;
        vis[x] = true;
        for(int i = head[x]; i; i = a[i].next) {
            int y = a[i].y;
            if(d[y] > d[x] + a[i].val) {
                d[y] = d[x] + a[i].val;
                p[y] = a[i].cnt;
                Q.push(heapnode(y,d[y]));
            }
        }
    }
    return;
}

void ask_print() {
    int x;
    scanf("%d", &x);
    for(vector<int> :: iterator it = Path[x].begin();it != Path[x].end(); it++)
        printf("%d ", *it);
    printf("\n\n");
}

Path tracing examples

Two questions, I have not done.

1, UVA11374 Airport Express (province of difficulty selected, refueling)

In the I * Iokh O k H ** in the city, the Airport Express is the preferred means of transport people from the city to the airport. Airport Express line is divided into economic and commercial lines are two kinds of lines, speed and price are different. Do you have a business line ticket, you can take a stand business lines, while other times only take economic lines. Assuming that transfer time is negligible, your task is to find a fastest route to the airport.
Input format:
input comprising a plurality of sets of data. Each test 33 integer N, S
N , S , and E (2 \ Leq N \ Leq 500,. 1 \ Leq S, E \ Leq 100) E (2 ≦ N ≤500,1≤ S , E ≤ 100), that the total number of stations, start and end points of the Airport Express line (ie, where the airport station) number. The next line contains an integer M (. 1 \ Leq M \ Leq 1000) M (1 ≦ M ≤1000), i.e., the number of sections of strip lines economic. The following M M lines each an integer of 3 X-, the Y, the Z (. 1 \ X-Leq, the Y \ N Leq,. 1 \ Leq the Z \ Leq 100) X- , the Y , the Z (1 ≦ X- , the Y N , 1 ≦ the Z≦ 100), indicates the economy can take the station line X- X- and station the Y the Y forth between which the required way the Z the Z minutes. Article behavior of commercial road next line number K (1 \ Leq K \ Leq 1000) K (1≤ K ≤1000), the following K K * line is these sections describe the same format as the economic line. All roads are bidirectional, but may have to use commercial ticket to get to the airport. The only guarantee the optimal solution.
Output format:
For each test, the output line 33. The first line gives the order of access through the various stations (including start and end), the second line is the transfer station number (if no ticket commercial line, the output line of business Ticket Not Used), the third line is the total time.

2, P2176 [USACO14FEB] roadblocks Roadblock (This question is difficult to think than the above, but the difficulty is increased)

Title Description

Every morning, FJ from their homes across the farm went to the bullpen. Agricultural farms consisting of N blocks, M are connected by bi-directional farm road, each road has a certain length. FJ house No. 1 field, the field number N in the cow. The two fields are not connected to a plurality of roads to the appropriate path for any pair of sequence always walk on the farm field. When another piece when FJ come from a field, always with a total path length of the shortest road to go order.

FJ cattle do, always ill-wishers, she decided to interfere with his plans each morning. They are placed in a stack of straw on a road M, so that doubling the length of the road. Cattle hope you follow a path interference makes FJ road from home to the largest increase in the bullpen long. They ask you to design and tell them how much the maximum increment.

Input and output formats

Input formats:

Line 1: two integers N, M.

2 through M + 1: Line i + 1 line contains three integers A_i, B_i, L_i, A_i and B_i i represents the number of fields of road connection, L_i represents path length.

Output formats:

Line 1: An integer representing the maximum increment by doubling a route obtained.

Sample input and output

Input Sample # 1:

5 7
2 1 5
1 3 1
3 2 8
3 5 7
3 4 3
2 4 7
4 5 2

Output Sample # 1:

2

Guess you like

Origin www.cnblogs.com/virtualtan/p/10974700.html
Recommended