记不清这是第多少次总结最短路问题了,不过每次总结也都能有新的收获吧。

这次是总结当作模板使用的。

Bellman算法:

维护一个数组,记录从起点到其他点的距离,不断通过可能的路径来更新数组,直到遍历了所有的路径,从而找到最小值。

// 最短路 Bellman-Ford 算法

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000+7;
const int INF = 0x3f3f3f3f;

struct edge{
    int from, to, cost;
};

int n, m;   // n个顶点,m条边
edge G[MAXN];
int dis[MAXN];

void Bellman()
{
    fill(dis, dis+MAXN, INF);
    dis[1] = 0;
    while(1) 
    { 
        bool update = false;
        for(int i=0; i<m; i++)
        {
            edge e = G[i];
            if(dis[e.from] != INF && dis[e.from] + e.cost < dis[e.to])
            {
                dis[e.to] = dis[e.from] + e.cost;
                update = true;
            }
        }
        if(!update)
            break;
    }
}


int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        for(int i=0; i<m; i++)
        {
            scanf("%d %d %d", &G[i].from, &G[i].to, &G[i].cost);
        }
        Bellman();
        printf("%d\n", dis[n]);
    }


    return 0;
}

 

Dijkstra算法:

每次取已经确定的最短路,在已知的最短路的基础上寻找下一个距离最近的点,知道找到所有的点。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1010;

int n, m;   // n个顶点, m条边
int G[N][N];    // 存图
bool used[N];   // 标记点是否走过
int dis[N];     // 起点到其他点的距离

void djk(int s)     // s为指定的一个起点
{
    // 首先要初始化
    for(int i=0; i<N; i++)
    {
        used[i] = 0;
        dis[i] = INF;
    }
    dis[s] = 0;     // 应该把起点的距离置为0

    while(1)
    {
        int v = -1;
        // 从未使用过的点中找到一个距离最近的点
        for(int i=1; i<=n; i++)
        {
            if(!used[i] && (v == -1 || dis[i] < dis[v]))
                v = i;
        }
        if(v == -1 )    break; // 如果所有的点都已经访问过了
        used[v] = 1;
        for(int i=1; i<=n; i++)
            dis[i] = min(dis[i], dis[v] + G[v][i]);

    }
}

int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        memset(G, INF, sizeof(G));
        int a, b, c;
        for(int i=1; i<=m; i++)
        {
            scanf("%d %d %d", &a, &b, &c);
            G[a][b] = c;
        }
        djk(1);
        printf("%d\n", dis[n]);
    }


    return 0;
}

 

优先队列优化的 Dijkstra算法:

Dijkstra算法每次都重新遍历了一遍所有的点来找距离最近的点,使用优先队列可以优化这一问题。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1010;

struct edge{        // 邻接表存图,一次存两个属性
    int to, cost;
};
typedef pair<int, int> P;   // 优先队列内的节点需要两个值,first是最小距离, second是顶点编号
int n, m;
vector<edge> G[N];  // 邻接表
int dis[N];     // 从起点到其他点的最短距离

void djk(int s)
{
    priority_queue<P, vector<P>, greater<P> > q;    // 优先队列
    // 首先初始化
    for(int i=0; i<N; i++)
        dis[i] = INF;
    dis[s] = 0;
    
    q.push(P(0, s));    // 把起点放入队列中
    while(!q.empty())
    {
        P p = q.top();  q.pop();
        int v = p.second;   // 取出顶点
        if(dis[v] < p.first)    
            continue;
        for(int i=0; i<G[v].size(); i++)    // 遍历所以与当前点有边的点
        {
            edge e= G[v][i];
            if(dis[e.to] > dis[v] + e.cost) 
            {
                dis[e.to] = dis[v] + e.cost;
                q.push(P(dis[e.to], e.to));
            }
        }
    }

}

int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        int a;
        edge b;
        for(int i=0; i<m; i++)
        {
            scanf("%d %d %d", &a, &b.to, &b.cost);
            G[a].push_back(b);
        }
        djk(1);
        printf("%d\n", dis[n]);
    }



    return 0;
}

 

上面都是单源最短路问题,接下来的是任意两点间的最短路问题。

Floyd算法:

// Floyd 任意两点最短路算法
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int INF = 0x3f3f3f3f;

int n, m;
int dp[N][N];

void floyd()
{    
    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]);

}

int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        for(int i=0; i<N; i++)  // 对邻接矩阵的初始化
        {
            for(int j=0; j<N; j++)
            {
                if(i == j)  dp[i][j] = 0;
                else dp[i][j] = INF;
            }
        }

        int a, b, c;
        for(int i=0; i<m; i++)
        {
            scanf("%d %d %d", &a, &b, &c);
            dp[a][b] = dp[b][a] = c;    // 无向图必须必须双向
        }
        floyd();
        printf("%d\n", dp[1][8]);
        printf("%d\n", dp[8][1]);
    }


    return 0;
}