Dijkstra

补充:求次短路,只需要多维护一个“短路”就行了;
但在维护次短路时,要记得把更新前的最短路与更新后的最短路swap一下,留着给次短路更新;

注意:不能处理负边权, 有负边权就换SPFA, 当然也不能求最长路。

以下代码以HDOJ 1874(畅通工程续) 为例
题目链接
简洁好理解的堆优化代码。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cctype>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;

typedef pair<int,int> pr;
const int maxn=205;
vector<pr> E[maxn];

int n, m;
int d[maxn], vis[maxn];
void init() {
    for(int i=0; i<maxn; ++i) E[i].clear();
    for(int i=0; i<maxn; ++i) d[i]=1e9, vis[i]=0;
}

int main() {
    while(cin>>n>>m) {
        init();
        for(int i=0; i<m; ++i) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            E[x].push_back(make_pair(y,z));
            E[y].push_back(make_pair(x,z));
        }
        int s, t;
        scanf("%d%d", &s, &t);
        priority_queue<pr,vector<pr>,greater<pr> > Q;
        Q.push(make_pair(0,s));
        d[s]=0;
        while(!Q.empty()) {
            int now=Q.top().second; Q.pop();
            if(vis[now]) continue; vis[now]=1;
            for(int i=0; i<E[now].size(); ++i) {
                int v=E[now][i].first;
                if(d[v]>d[now]+E[now][i].second) {
                    d[v]=d[now]+E[now][i].second;
                    Q.push(make_pair(d[v],v));
                }
            }
        }
        if(d[t]==1e9) printf("-1\n");
        else printf("%d\n", d[t]);
    }
}

也可不用greater函数,直接将每次放入队列的d[v]改成-d[v]即可。

SPFA

SLF优化(Small Label First 策略):使用双端队列,当需要push节点进队列时,判断一下当前dis与队首dis大小;若更小,则push_front();否则push_back()。

LLL优化(Large Label Last 策略):如果当前出队的元素dis大于队内平均值,则先push到队尾,考虑下一个元素。

SPFA最短路

以下代码以HDOJ 1874(畅通工程续) 为例
题目链接
注意有个inq数组来记录某个点是否已经在队列了,这是与dij不同的。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cctype>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;

const int maxn=205;
vector<pair<int, int> >E[maxn];

int n, m;
int d[maxn], inq[maxn];
void init() {
    for(int i=0; i<maxn; ++i) E[i].clear();
    for(int i=0; i<maxn; ++i) inq[i]=0;
    for(int i=0; i<maxn; ++i) d[i]=1e9;
}

int main() {
    while(cin>>n>>m) {
        init();
        for(int i=0; i<m; ++i) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            E[x].push_back(make_pair(y,z));
            E[y].push_back(make_pair(x,z));
        }
        int s, t;
        scanf("%d%d", &s, &t);
        queue<int> Q;
        Q.push(s);
        d[s]=0;
        inq[s]=1;
        while(!Q.empty()) {
            int now=Q.front();
            Q.pop();
            inq[now]=0;
            for(int i=0; i<E[now].size(); ++i) {
                int v=E[now][i].first;
                if(d[v]>d[now]+E[now][i].second) {
                    d[v]=d[now]+E[now][i].second;
                    if(!inq[v]) {
                    	inq[v]=1;
                    	Q.push(v);
                    	// 在此处加入判负环代码
                    }
                }
            }
        }
        if(d[t]==1e9) printf("-1\n");
        else printf("%d\n", d[t]);
    }
}

SPFA判断负环

在上述代码指定位置处加入: if(++cnt[v]>n) flag=1
仅仅需要一个cnt[]数组和一个负环flag即可.

SPFA最长路

以下代码以HDOJ 1224 (Free DIY Tour)为例

题目链接在此

原理:仅将邻接矩阵的权值定为取负即可(所有d[i]仍初始化为正inf)。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cctype>
#include<string>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;

const int maxn=110;

int N;
int city[maxn], inq[maxn], d[maxn], path[maxn];
vector<pair<int,int> > lj[maxn];

void init() {
    memset(inq, 0, sizeof(inq));
    memset(path, 0, sizeof(path));
    for(int i=0; i<maxn; ++i) lj[i].clear();
    for(int i=0; i<maxn; ++i) d[i]=1e7;
}

void print(int p) {
    if(p==1) return;
    print(path[p]);
    if(p==N+1) printf("->1\n");
    else printf("->%d", p);
}
int main() {
    int T, t;
    scanf("%d", &T);
    t=T;
    while(T--) {
        scanf("%d", &N);
        init();
        for(int i=1; i<=N; ++i) {
            scanf("%d", &city[i]);
            city[i]=-city[i];
        }
        city[N+1]=city[1];
        int m;
        scanf("%d", &m);
        while(m--) {
            int a, b;
            scanf("%d%d", &a, &b);
            lj[a].push_back(make_pair(b,city[b]));
        }
        queue<int> Q;
        Q.push(1);
        d[1]=0;
        inq[1]=1;
        while(!Q.empty()) {
            int now=Q.front();
            inq[now]=0;
            Q.pop();
            for(int i=0; i<lj[now].size(); ++i) {
                int n=lj[now][i].first;
                if(d[now]+lj[now][i].second<d[n]) {
                    d[n]=d[now]+lj[now][i].second;
                    path[n]=now;
                    if(inq[n]) continue;
                    Q.push(n);
                    inq[n]=1;
                }
            }
        }
        printf("CASE %d#\n", t-T);
        printf("points : %d\n", -d[N+1]);
        printf("circuit : 1");
        print(N+1);
        if(T) puts("");
    }
    return 0;
}


Floyd

总所周知 简单粗暴
以下为floyd的路径保存问题代码, 并且附带倒序输出路径
若想要正序输出, 则先将路径弄到栈里面再弹出

void floyd() {
    for(int i=0; i<n; ++i) {
        for(int j=0; j<n; ++j) {
            if(d[i][j]==inf) path[i][j]=-1;
            else path[i][j]=i;
        }
    }
    for(int k=0; k<n; ++k) {
        for(int i=0; i<n; ++i) {
            for(int j=0; j<n; ++j) {
                if(!(d[i][k]==inf||d[k][j]==inf)&&d[i][k]+d[k][j]<d[i][j]) {
                    d[i][j]=d[i][k]+d[k][j];
                    path[i][j]=path[k][j];
                }
            }
        }
    }
}
void printpath(int from, int to) {
    printf("%d ", to);
    while(path[from][t]!=from) {
        printf("%d ", path[from][to]);
        to=path[from][to];
    }
}

Bellman-Ford

SPFA就是这个算法的队列优化。
同样可以用于判负环(循环了n次及以上,说明有负环)

const int maxn = 1e4+10;
const int maxe = 1e6+10; //最大边数
const int INF = 1e9;

struct edge{
    int from, to, cost;
    edge(int f=0, int t=0, int c=0):from(f),to(t),cost(c) {}
};

edge es[maxe];
int d[maxn];
int n, E; //顶点数, 边数

void Bellman_Ford(int s) {
    for(int i=0; i<n; ++i) d[i]=INF;
    d[s]=0;
    while(1) {
        bool update=0;
        for(int i=0; i<E; ++i) {
            edge e=es[i];
            if(d[e.from]!=INF && d[e.to]>d[e.from]+e.cost) {
                d[e.to]=d[e.from]+e.cost;
                update=1;
            }
        }
        if(!update) break;
    }
}