最短路那点事

“它死了…”——王队长


最短路算法有很多很多种,我所知的有三种:
1.DIJ
2.SPFA
3.Bellman-ford

由于第三种算法的复杂度是 O ( n m ) ,且实现过于简单,可以用上限也是 O ( n m ) 的SPFA替换,所以就不提了。

这篇博客主要是简略分析最短路算法的时间以及小用法。

朴素SPFA:

没什么好讲的。

SPFA判负环:

BFS:

一个点进队超过n次则有负环。

DFS:

初始化dis为0。
若一个点重复走了两次,则有负环。

实测DFS快很多。

优化1:

循环队列:
针对空间限制小,时间限制大的最短路题目。

一般用不上,你能用上你也TLE了。

优化2(来自JBY09年集训队论文):

连续增广。

若x更新y,且y不在队列里,要把y加入队列。

把y和当前的队列头对比一下,若y的dis更小,则把y提前。

优化3(来自JBY09年集训队论文):

随机调整边的顺序。

然而这个并没有什么卵用。

因为如果我的数据就是类随机造的,你再怎么随机也没用。

优化4:

受JBY启发,我想了一个随机算法。

随机队列中的一个点,把它于当前队列头比较,若它的dis更小,则交换。

实测效果良好。

优化5:

为什么要随机啊,直接堆不行吗?

然后你发现它就变成类DIJ了。

假优化:

递归版SPFA。

慎用!可以卡到 O ( 2 n ) ,这个得看图而定了。


“稳定!稳定!稳定!我们ACMer追求的是稳!”

这是GDOI复评时凡喵对我说的(不听老人言,吃亏在后来)。


如何造数据卡掉SPFA:

显然把整个图拉长,这样的话每次让你白更新,你就变成 O ( n m ) 的了。

比如说每个点只往序号±10的点连边。

Code:

#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int rand(int x, int y) {
    return (RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}

int n, m, c;

void write(int x) {
    if(x == 0) {putchar('0'); return;}
    int d[10]; d[0] = 0;
    for(; x; x /= 10) d[++ d[0]] = x % 10;
    for(; d[0]; d[0] --) putchar(d[d[0]] + '0');
}

void put(int x, int y, int z) {
    write(x); putchar(' '); write(y); putchar(' '); write(z); putchar('\n');
}

int main() {
    srand((unsigned) time (0));
    freopen("a.in", "w", stdout);
    n = 40000; m = 1000000;
    printf("%d %d\n", n, m);
    fo(i, 1, n - 1) put(i, i + 1, rand(1, 2e9));
    fo(i, 1, m - (n - 1)) {
        int x = rand(1, n);
        int y = rand(max(1, x - 10), min(n, x + 10));
        put(x, y, rand(1, 2e9 / n));
    }
}

对比:

代码放最后。

朴素SPFA:上天。

连续增广SPFA:4s

随机换点SPFA:0.8s

set优化DIJ:0.7s

Code(朴素):

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int rand(int x, int y) {
    return (RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}

const int N = 2e6 + 5;

int n, m, x, y, z;
int final[N], to[N], next[N], v[N], tot;

void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, v[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, v[tot] = z, final[y] = tot;
}

void read(int &x) {
    char c = ' ';
    while(c < '0' || c > '9') c = getchar();
    x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}

int dis[N], bz[N], d[N * 10];

int main() {
    srand((unsigned) time (0));
    freopen("a.in", "r", stdin);
    scanf("%d %d", &n, &m);
    fo(i, 1, m) {
        read(x); read(y); read(z);
        link(x, y, z);
    }
    fo(i, 1, n) dis[i] = 2e9;
    dis[1] = 0; bz[1] = 1; d[d[0] = 1] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        for(int j = final[x]; j; j = next[j]) {
            int y = to[j], z = v[j];
            if(dis[x] + z < dis[y]) {
                dis[y] = dis[x] + z;
                if(!bz[y]) {
                    d[++ d[0]] = y, bz[y] = 1;
                }
            }
        }
        bz[x] = 0;
    }
}

Code(连续增广):

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int rand(int x, int y) {
    return (RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}

const int N = 2e6 + 5;

int n, m, x, y, z;
int final[N], to[N], next[N], v[N], tot;

void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, v[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, v[tot] = z, final[y] = tot;
}

void read(int &x) {
    char c = ' ';
    while(c < '0' || c > '9') c = getchar();
    x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}

int dis[N], bz[N], d[N * 10];

int main() {
    srand((unsigned) time (0));
    freopen("a.in", "r", stdin);
    scanf("%d %d", &n, &m);
    fo(i, 1, m) {
        read(x); read(y); read(z);
        link(x, y, z);
    }
    fo(i, 1, n) dis[i] = 2e9;
    dis[1] = 0; bz[1] = 1; d[d[0] = 1] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        for(int j = final[x]; j; j = next[j]) {
            int y = to[j], z = v[j];
            if(dis[x] + z < dis[y]) {
                dis[y] = dis[x] + z;
                if(!bz[y]) {
                    d[++ d[0]] = y, bz[y] = 1;
                    if(dis[y] < dis[d[i + 1]]) swap(d[i + 1], d[d[0]]);
                }
            }
        }
        bz[x] = 0;
    }
}

Code (随机换点):

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int rand(int x, int y) {
    return (RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}

const int N = 2e6 + 5;

int n, m, x, y, z;
int final[N], to[N], next[N], v[N], tot;

void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, v[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, v[tot] = z, final[y] = tot;
}

void read(int &x) {
    char c = ' ';
    while(c < '0' || c > '9') c = getchar();
    x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}

int dis[N], bz[N], d[N * 10];

int main() {
    srand((unsigned) time (0));
    freopen("a.in", "r", stdin);
    scanf("%d %d", &n, &m);
    fo(i, 1, m) {
        read(x); read(y); read(z);
        link(x, y, z);
    }
    fo(i, 1, n) dis[i] = 2e9;
    dis[1] = 0; bz[1] = 1; d[d[0] = 1] = 1;
    fo(i, 1, d[0]) {
        int num = rand(i, d[0]);
        if(dis[d[num]] < dis[d[i]]) swap(d[i], d[num]);
        int x = d[i];
        for(int j = final[x]; j; j = next[j]) {
            int y = to[j], z = v[j];
            if(dis[x] + z < dis[y]) {
                dis[y] = dis[x] + z;
                if(!bz[y]) {
                    d[++ d[0]] = y, bz[y] = 1;
                }
            }
        }
        bz[x] = 0;
    }
}

Code(set优化DIJ):

#include<set>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define P pair<int, int>
#define mp make_pair
#define fi first
#define se second
using namespace std;

int rand(int x, int y) {
    return (RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}

const int N = 2e6 + 5;

int n, m, x, y, z;
int final[N], to[N], next[N], v[N], tot;

void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, v[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, v[tot] = z, final[y] = tot;
}

void read(int &x) {
    char c = ' ';
    while(c < '0' || c > '9') c = getchar();
    x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}

int dis[N], bz[N];

multiset<P> s;

int main() {
    srand((unsigned) time (0));
    freopen("a.in", "r", stdin);
    scanf("%d %d", &n, &m);
    fo(i, 1, m) {
        read(x); read(y); read(z);
        link(x, y, z);
    }
    memset(bz, 0, sizeof bz);
    fo(i, 1, n) dis[i] = 2e9; dis[1] = 0;
    fo(i, 1, n) s.insert(mp(dis[i], i));
    fo(i, 1, n) {
        P z = *s.begin(); s.erase(s.begin());
        int x = z.se;
        for(int j = final[x]; j; j = next[j]) {
            int y = to[j], z = v[j];
            if(dis[x] + z < dis[y]) {
                s.erase(s.find(mp(dis[y], y)));
                dis[y] = dis[x] + z;
                s.insert(mp(dis[y], y));
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Cold_Chair/article/details/81121595