图论——最短路径

Posted 牧空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论——最短路径相关的知识,希望对你有一定的参考价值。

Dijkstra算法

原理

贪心和线性规划
将顶点集分为S和T

  • S:已经确定的顶点集合,初始只含源点s
  • T:尚未确定的顶点集合

反复从T中选取当前到源点s最近的顶点u,将顶点u加入集合S,然后对所有从u出发的边进行松弛操作1。这是典型的贪心策略,且该问题具备无后效性,所以可以收敛到最优解。

算法步骤

  1. 初始时,S只包含起点s;T包含除s外的其他顶点,且T中顶点的距离为"起点s到该顶点的距离",不可达就是无穷大
  2. 从T中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从T中移除顶点k。
  3. 更新T中各个顶点到起点s的距离。之所以更新T中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离
  4. 重复步骤(2)和(3),直到遍历完所有顶点。

优化:
在T中寻找最近的顶点,可以维护一个优先队列,将集合T中的顶点到源点的s的距离作为优先级,距离越近,优先级越高。将选择最近的顶点的时间复杂度从 O ( V ) O(V) O(V)降到 O ( l o g V ) O(logV) O(logV),这样的操作要执行 ∣ E ∣ |E| E次,建立优先队列的时间复杂度为 O ( V ) O(V) O(V),那么总的时间复杂度为 O ( ( E + V ) l o g V ) = O ( E l o g V ) O((E+V)logV)=O(ElogV) O((E+V)logV)=O(ElogV)。因为 E E E V 2 V^2 V2是一个数量级的,也就是说 O ( E l o g V ) = O ( V 2 l o g V ) > O ( V 2 ) O(ElogV)=O(V^2logV)>O(V^2) O(ElogV)=O(V2logV)>O(V2),那就没有优化一说。但是在大多数情况下图为稀疏图, ∣ E ∣ < < V 2 |E|<<V^2 E<<V2甚至和 V V V是一个数量级的,所以对于稀疏图来说,时间复杂度更接近 O ( V l o g V ) O(VlogV) O(VlogV)

例题

OJ——最短路径问题

描述
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入描述:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。 (1<n<=1000, 0<m<100000, s != t)
输出描述:
输出 一行有两个数, 最短距离及其花费。

!!非AC代码,有问题,对于非简单图有bug

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

#define MAXN 1001
#define INF INT_MAX

using namespace std;

struct Edge
{
    int to;
    int lenght;
    int price;
    Edge(int t, int l, int c) : to(t), lenght(l), price(c) {}
};

struct Point
{
    int number;
    int distance;   // 点到源点的距离
    // 即使使用cost[]数组记录了各点到源点的花费,
    // 但是因为维护了一个优先队列,所以还需要比较点到源点的花费,才能保证队首的是最近且花费最小点
    int cost;       // 点到源点的消费
    Point(int n, int d, int c) : number(n), distance(d), cost(c) {}
    // 距离更近,优先级越高;花费越低,优先级越高
    bool operator<(const Point &p) const
    {
        if(distance != p.distance)
            return distance > p.distance;
        else return cost > p.cost;
    }
};

vector<Edge> graph[MAXN];
int dis[MAXN];
int cost[MAXN];

void Dijkstra(int s)
{
    priority_queue<Point> myQueue;
    dis[s] = 0;
    cost[s] = 0;
    myQueue.push(Point(s, dis[s], 0));
    while (!myQueue.empty())
    {
        // 获取最近点
        int u = myQueue.top().number;
        myQueue.pop();
        for (int i = 0; i < graph[u].size(); i++)
        {
            int v = graph[u][i].to;
            int l = graph[u][i].lenght;
            int p = graph[u][i].price;
            //距离相等时比较cost,不等是就比较距离
            if ((dis[v] == dis[u] + l && cost[v] > cost[u + p]) || dis[v] > dis[u] + l)
            {
                dis[v] = dis[u] + l;
                cost[v] = cost[u] + p;
                myQueue.push(Point(v, dis[v], cost[v]));
            }
        }
    }
    return;
}

int main(int argc, char const *argv[])
{
    int n, m;
    while (scanf("%d %d", &n, &m) != EOF)
    {
        if (n == 0 && m == 0)
            break;
        memset(graph, 0, sizeof(graph));
        fill(dis, dis + n + 1, INF);
        fill(cost, cost + n + 1, INF);
        while (m--)
        {
            int from, to, length, price;
            scanf("%d%d%d%d", &from, &to, &length, &price);
            graph[from].push_back(Edge(to, length, price));
            graph[to].push_back(Edge(from, length, price));
        }
        int s, t; //起点和终点
        scanf("%d%d", &s, &t);
        Dijkstra(s);
        printf("%d %d\\n", dis[t], cost[t]);
    }

    return 0;
}


  1. 松弛操作也就是更新目前最近距离,相当于对长边松弛了 ↩︎

以上是关于图论——最短路径的主要内容,如果未能解决你的问题,请参考以下文章

图论之最短路径floyd算法

图论算法之最短路径

彻底弄懂最短路径问题(图论)

图论Dijkstra Algorithm在2D空间平面网格节点图选择最短路径,networkx,Python

图论 最短路径算法 1.Floyed-Warshall算法

算法总结图论-最短路径