图论——最短路径
Posted 牧空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论——最短路径相关的知识,希望对你有一定的参考价值。
Dijkstra算法
原理
贪心和线性规划
将顶点集分为S和T
- S:已经确定的顶点集合,初始只含源点s
- T:尚未确定的顶点集合
反复从T中选取当前到源点s最近的顶点u,将顶点u加入集合S,然后对所有从u出发的边进行松弛操作1。这是典型的贪心策略,且该问题具备无后效性,所以可以收敛到最优解。
算法步骤
- 初始时,S只包含起点s;T包含除s外的其他顶点,且T中顶点的距离为"起点s到该顶点的距离",不可达就是无穷大
- 从T中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从T中移除顶点k。
- 更新T中各个顶点到起点s的距离。之所以更新T中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离
- 重复步骤(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)
例题
描述
给你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;
}
松弛操作也就是更新目前最近距离,相当于对长边松弛了 ↩︎
以上是关于图论——最短路径的主要内容,如果未能解决你的问题,请参考以下文章