对最短路径问题(1)的补充
Bellman-Ford Algorithm(贝尔曼福德算法)
相对于迪杰斯特拉算法,它在没有负圈的情况最多更新V-1次,因此它适用于存在负边的情况,并且可以判断是否存在负圈(判断是否更新了V次以上)。
代码实现:
//贝尔曼福德算法 O(VE) 适用于负边
//下面例子是对DAG进行操作
#include<iostream>
#include<cstdio>
#include<vector>
#define MAX_V 100
#define MAX_E 100
#define INF 0x3f3f3f3f
using namespace std;
struct edge{
int from, to, cost;
};
int d[MAX_V];
edge es[MAX_E];//DAG,无需构成邻接矩阵或邻接表,直接写入边即可
int V, E;
void shortest_path(int s){//从顶点s出发
for(int i = 0; i < V; i++) d[i] = INF;
d[s] = 0;
while(true){//最多执行V-1次 若进行了V次以上,则存在负圈
bool flag = false;
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;
flag = true;
}
}
if(!flag) break;
}
}
int main(){
cin >> V >> E;
for(int i = 0; i < E; i++) cin >> es[i].from >> es[i].to >> es[i].cost;
shortest_path(0);
for(int i = 0; i < V; i++) cout << d[i] << " ";
return 0;
}
堆优化
对迪杰斯特拉算法进行堆优化
要点:
利用优先队列,在更新最短距离时,把对应元素往根的方向移动以满足堆的性质
每次push都会push到适当的位置,保持堆的稳定,“排序为从小到大”,下次取时总会取出min
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<numeric>
#define MAX_V 100
#define INF 0x3f3f3f3f
using namespace std;
struct edge{
int to, cost;
};
typedef pair<int, int> P;//first为最短距离,second是顶点编号
int V, E;
//邻接表的vector表示法
vector<edge> G[MAX_V];
int d[MAX_V];//d[i] 表示从 顶点s 到 顶点i 的最短距离
void Shortest_Path(int s){
//great<~>表示queue从小到大排列,而queue是队首出列,则是从小到大取出
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d+V, INF);
d[s] = 0;
que.push(P(0, s));
while(!que.empty()){
P p = que.top(); que.pop();
//每次取出的是最小值
int v = p.second;
if(d[v] < p.first) continue;//通往v的最小值已经小于p.first 返回继续pop下一个
for(int i = 0; i < G[v].size(); i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){//v即from
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
/*
在更新最短距离时,把对应元素往根的方向移动以满足堆的性质
每次push都会push到适当的位置,保持堆的稳定,"排序为从小到大",下次取时总会取出min
*/
}
}
}
}
int main(){
cin >> V >> E;
for(int i = 0; i < E; i++){
int v1, v2, w;
edge e1, e2;
cin >> v1 >> v2 >> w;
e1.to = v2; e1.cost = w;
e2.to = v1; e2.cost = w;
G[v1].push_back(e1);
G[v2].push_back(e2);
}
Shortest_Path(0);
for(int i = 0; i < V; i++) cout << d[i] << " ";
return 0;
}