本文出自我的掘金博客, 欢迎大家访问传送门
dijikstra
算法, 算法的思想我想不必介绍了, 大家都知道dijkstra的无优化版本核心代码
if(map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
//其实就是一个松弛操作而已嘛,这就是Dijkstra与Floyd的核心思想,Dijkstra就是把时间复杂度降低,范围缩小而已
typedef pair pa;
priority_queue , cmp > q;
struct cmp
{
bool operator()(const pa p1, const pa p2)
{
return p1.second > p2.second; //second的小值优先
}
};
//这里注意要传入比较函数, 由于pair的第二个值代表着距离, 且要求小根堆, 故这样写cmp函数
struct Edge {
int next;
int to;
int w;
} e[100000]; //这里至少要开双倍那么大, 因为是无向图, 双向都要加边哒, 我在这里踩过坑
其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.
此外, 我们还需要一个
head
数组, 它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实在以i为起点的所有边的最后输入的那个编号.你模拟一遍这个过程就会知道为什么, 因为后面的会覆盖前面的.
cnt
.void addEdge(int u, int v, int w) {
e[++cnt].w = w;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt; //head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.
}
dijkstra
函数, 注释已经很详细了void dijkstra () {
priority_queue , cmp > q;//初始化优先队列
q.push(make_pair(s, 0)); //将源点放入优先队列
//初始化为一个很大的值
for (int i = 1; i <= n; i++) {
dis[i] = 2e9;
}
//将源点赋值为0
dis[s] = 0;
while (!q.empty()) {
int now = q.top().first;
int val = q.top().second;
q.pop();
//遍历每一个顶点相连的所有边, 并进行松弛操作
for (int i = head[now]; i; i = e[i].next) {
int to = e[i].to;
//松弛操作不解释
if (dis[to] > e[i].w + val) {
dis[to] = e[i].w + val;
q.push(make_pair(to, dis[to]));
}
}
}
}
Dijkstra
算法优化版本的完整代码//打算尝试一下Dijkstra算法的链式前向星写法以及堆优化
#include
using namespace std;
typedef pair pa;
int n, m, s, t, head[10000], cnt, dis[10000];
//建立边结构体,其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.
struct Edge {
int next;
int to;
int w;
} e[100000]; //这里至少要开双倍那么大, 因为是无向图, 双向都要加边哒
struct cmp
{
bool operator()(const pa p1, const pa p2)
{
return p1.second > p2.second; //second的小值优先
}
};
void addEdge(int u, int v, int w) {
e[++cnt].w = w;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt; //head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.
}
void dijkstra () {
priority_queue , cmp > q;
q.push(make_pair(s, 0)); //将源点放入优先队列
for (int i = 1; i <= n; i++) {
dis[i] = 2e9;
}
dis[s] = 0;
while (!q.empty()) {
int now = q.top().first;
int val = q.top().second;
q.pop();
for (int i = head[now]; i; i = e[i].next) {
int to = e[i].to;
if (dis[to] > e[i].w + val) {
dis[to] = e[i].w + val;
q.push(make_pair(to, dis[to]));
}
}
}
}
int main() {
cin >> n >> m >> s >> t;
int u, v, w;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
addEdge(u, v, w);
addEdge(v, u, w);
}
dijkstra();
cout << dis[t];
return 0;
}
Bellman-Ford
算法以下操作循环执行至多n-1次,n为顶点数:
对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
dis[s] = 0;
for (int i = 1; i < n; i++) {
check = true;
for (int i = 1; i <= m; i++) {
if (dis[v[i]] > dis[u[i]] + w[i]) {
check = false;
dis[v[i]] = dis[u[i]] + w[i];
}
if (dis[u[i]] > dis[v[i]] + w[i]) {
check = false;
dis[u[i]] = dis[v[i]] + w[i];
}
}
if (check) {
break;
}
}
check
的作用就是当没有可以松弛的点时, 就说明算法以及结束, 不用遍历n- 1
轮了, 直接跳出算法#include
using namespace std;
const int MAXN = 2500 + 10;
const int MAXM = 6200 + 10;
const int INF = 2e9;
int u[MAXM], v[MAXM], dis[MAXN], w[MAXM];
bool check;
int n, m, s, t;
int main() {
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
}
for (int i = 1; i <= n; i++) {
dis[i] = INF;
}
dis[s] = 0;
for (int i = 1; i < n; i++) {
check = true;
for (int i = 1; i <= m; i++) {
if (dis[v[i]] > dis[u[i]] + w[i]) {
check = false;
dis[v[i]] = dis[u[i]] + w[i];
}
if (dis[u[i]] > dis[v[i]] + w[i]) {
check = false;
dis[u[i]] = dis[v[i]] + w[i];
}
}
if (check) {
break;
}
}
cout << dis[t];
return 0;
}