”明月如霜,好风如水,清景无限 “
总的来说,最短路径是图论的最常见的问题。即在一副有向图(无向图是特殊的有向图,不做考虑。记图中的结点数N ,而边数为 M,边长记为W)中找到其中两点的路径最短值。
壹
-
复杂度 :O(n * n),分析可知遍历为n,更新为n。 -
特点介绍:看复杂度可以知道,此算法仅仅与结点个数有关。那么当图的边数密集,是稠密图时,将会非常适合。而对于稠密图,储存方式最好是邻接矩阵。其中dijkstra算法对应的边长权值都为正数。 -
代码关键点:
(1).定义最短路的集合,及将最短路的结点放入其中。及认为st[t] = true;
(2).从1到n遍历
(3).找到离结点1,最近的结点记为t,将放入最短路集合
(4).通过t来更新其他结点j的信息,及min(dist[j] , dist[t] + g[t][j])
const int N = 510;
int n , m;
int g[N][N];
int dist[N];
bool st[N];
?
int dijkstra(){
memset(dist , 0x3f , sizeof(dist));
dist[1] = 0;
for(int i = 0 ; i < n ; ++i){
int t = -1;
for(int j = 1 ; j <= n ; ++j){
if(!st[j] && (t == -1 ||dist[t] > dist[j]))
t = j;
}
st[t] = true;
for(int j = 1 ; j < n ; ++j)
dist[j] = min(dist[j] , dist[t] + g[t][j]);
}
return (dist[n] == 0x3f3f3f3f) ? -1:dist[n];
}
贰
-
复杂度 :O(m * log n),分析可知优先队列找n次最小值复杂度为n,而更新m条边的最短距离复杂度为 m * log n。 -
特点介绍:因为发现找到离结点1,最近的结点记为t,此过程复杂度较高,因此换为小根堆(优先队列)存储,则复杂度变为O(1),而修改其中一条边的值,复杂度为O(log n )。 -
代码关键点:和上述一样,但是因为分析算法复杂度,及为稀疏图时(边数m小),则算法复杂度低,图的存储方式用邻接链表。注意小根堆的定义方式:priority_queue<PAI , vector , greater> heap;PAI表示的是<1到结点t的距离,结点t>
记得在入队时,先将dist[j]更新,再入队
typedef pair<int , int> PAI;
const int N = 510;
int n , m;
int e[N] , ne[N] , h[N] , w[N], idx;
int dist[N];
bool st[N];
void add(int a, int b , int c){
e[idx] = b ;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
?
int dijkstra(){
memset(dist , 0x3f , sizeof(dist));
dist[1] = 0;
?
priority_queue<PAI , vector<PAI> , greater<PAI>> heap;
heap.push({0 , 1});
while(!heap.empty()){
auto t = heap.top();
heap.pop();
int distance = t.first , vec = t.second;
if(st[vec]) continue;
for(int i = h[vec] ; i != -1 ; i = ne[i]){
int j = e[i];
if(dist[j] > distance + w[i]) {
dist[j] = distance + w[i];
heap.push({dist[j] , j});
}
}
}
return (dist[n] == 0x3f3f3f3f) ? -1:dist[n];
}
叁
-
复杂度 :O(m * k),遍历为m(准确为k)次,边数为m, 因此复杂度为m * k。 -
特点介绍:当图中有负权边时,可以使用。但是最大的优势是可以限制边的数量,及找结点n到结点1在最多经过k条边的情况下的最短距离。其中因为边的存储可以是结构体,定义边的三个属性起点a,终点b,权值w。如果限制最大边数k,那么必须保证在更新结点b的距离时,保证结点a的距离是上一轮的,即:
dist[b] = min(dist[b] , backup[a] + w);
const int N = 510 , M = 10010;
int n , m , k;
int dist[N] , backup[N];
?
struct Edge
{
int a,b,w;
}edge[M];
?
?
int bellman_ford(){
memset(dist , 0x3f , sizeof(dist));
dist[1] = 0;
for(int i = 0 ; i < k ;++i){
memcpy(backup , dist , sizeof(dist));
for(int j = 0 ; j < m ; ++j){
int a = edge[j].a , b = edge[j].b , w = edge[j].w;
dist[b] = min(dist[b] , backup[a] + w);
}
}
return dist[n];
}
?
- 代码关键点:即使有负环,某些点最短路也不是-∞,有k条边数限制:
肆
dist[b] = min(dist[b] , backup[a] + w);
const int N = 510;
int n , m;
int e[N] , ne[N] , h[N] , w[N], idx;
int dist[N];
bool st[N];
?
void add(int a, int b , int c){
e[idx] = b ;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
?
int spfa(){
memset(dist , 0x3f , sizeof(dist));
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
?
while(q.size()){
int t = q.front();
q.pop();
st[t] = false;
?
for(int i = h[t] ; i != -1 ; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + w[i]){
dist[j] = dist[t] + w[i];
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return (dist[n] == 0x3f3f3f3f) ? -1:dist[n];
}
肆
const int N = 1010;
const int INF = 1e9;
?
int d[N][N];
int n , m;
?
void print_d(){
cout<<"d = "<<endl;
for(int i = 1 ; i <= n ; ++i){
for(int j = 1 ; j <= n ; ++j){
cout<<d[i][j]<<" ";
}
cout<<endl;
}
}
void init_floyd(){
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= n ; ++j)
d[i][j] = (i == j) ? 0 : INF;
print_d();
}
int floyd(){
for(int k = 1 ; k <= n ; ++k)
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= n ; ++j)
d[i][j] = min(d[i][j] , d[i][k] +d[k][j]);
return d[1][n];
}
源码点击阅读原文,记得点赞关注。
END
作者:不爱跑马的影迷不是好程序猿
喜欢的话请关注点赞👇 👇👇 👇
公众号 : 文远的学习笔记
爱上层楼,浅斟低唱;酒醒何处,浮生若梦。
壹句: “C 艹 mother fucker \n”
|