SPFA算法

这一算法的核心思想是通过两边之和大于第三边不断逼近两个点的距离,最后得到最短距离。再通过不断的入列出列,最后使距离不能再小,得到最小距离。



邻接矩阵

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 210;
int q[maxn],dis[maxn],visit[maxn],a[maxn][maxn],b[maxn][maxn];
int n,m,s,t;
void spfa() {
	for(int i = 1; i <= n; i++)	dis[i] = inf;//先不断的初始化距离为最大值
	dis[s] = 0; visit[s] = 1; q[1] = s;//第一次入列的要取出发点。
	int head = 0, tail = 1;//队列的头尾,
	while(head < tail) {//队列不为空, 
		head++;
		int v = q[head];//取队列头,
		visit[v] = 0;//取消标记,通过重复入列得到最小距离,
		for(int i = 1; i <= b[v][0]; i++)//前面输入处理得到的边数,
			if(dis[b[v][i]] > dis[v] + a[v][b[v][i]]) {//满足可缩减,
				dis[b[v][i]] = dis[v] + a[v][b[v][i]];//缩减距离
				if(!visit[b[v][i]]) {//判断这个点是否能入列。
					q[++tail] = b[v][i];
					visit[b[v][i]] = 1;
				}
			} 
	}
}
int main() {
	int x,y,z;
	cin >> n >> m;
	for(int i = 0; i < m; i++) {
		cin >> x >> y >> z;
		if(a[x][y] != 0 && z > a[x][y])	continue;//保证都进来的边是最小值
		b[x][++b[x][0]] = y; a[x][y] = z;//b[x][0]记录以x为起点的边数,下面同理 
		b[y][++b[y][0]] = x; a[y][x] = z; 
	}
	cin >> s >> t;//计算的是 s 到 t 的距离,
	spfa();
	if(dis[t] != inf)	cout << dis[t] <<endl;
	else	cout << "-1" <<endl;
	return 0;
}

dfs优化
我们可以知道,这种入列迭代的方法每次入列只能优化一个点,我们能否通过这一个点继续取优化与其相关的其他点呢,答案显然是可以的,每次优化到一个点的时候,再入列的那一步通过再一轮for循环继续取优化与其相关的点,但是下一个点又有与其相关的点,于是我们可以想到用递归来优化它,于是我们有了下面的思路,

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 210;
int dis[maxn],visit[maxn],a[maxn][maxn],b[maxn][maxn];
int n,m,s,t;
void spfa(int v) {
	for(int i = 1; i <= b[v][0]; i++) {
		if(dis[b[v][i]] > dis[v] + a[v][b[v][i]]) {
			dis[b[v][i]] = dis[v] + a[v][b[v][i]];
			spfa(b[v][i]);
		}
	}
}
int main() {
	int x,y,z;
	cin >> n >> m;
	for(int i = 0; i < m; i++) {
		cin >> x >> y >> z;
		if(a[x][y] != 0 && z > a[x][y])	continue;//保证都进来的边是最小值
		b[x][++b[x][0]] = y; a[x][y] = z;//b[x][0]记录以x为起点的边数,下面同理 
		b[y][++b[y][0]] = x; a[y][x] = z; 
	}
	cin >> s >> t;
	for(int i = 1; i <= n; i++)	dis[i] = inf;
	dis[s] = 0;
	spfa(s);
	if(dis[t] != inf)	cout << dis[t] <<endl;
	else	cout << "-1" <<endl;
	return 0;
}


前面的算法貌似已经很完美了,但是我们能否优化一下内存,使程序的占用空间再小一点,这里我们介绍一下链式向前星存图。

struct edge {
	int to,next,value;
};

to代表这条边的终点,next代表和这条边同起点的下一条边的数组下标,value是这条边的权重
我们有了下面的两个优化的代码。
非DFS

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e4 + 10;
struct edge {
	int to,value,next;
}a[maxn];
int cnt = 1,n,m,s,t,head[maxn],dis[maxn],q[maxn],visit[maxn];
void add(int x,int y ,int z) {
	a[cnt].to = y;
	a[cnt].value = z;
	a[cnt].next = head[x];
	head[x] = cnt++;
}
void spfa() {
	for(int i = 1; i <= n; i++)	dis[i] = inf;
	dis[s] = 0; q[1] = s; visit[s] = 1;
	int front = 0,tail = 1;
	while(front < tail) {
		front ++;
		int v = q[front];
		visit[v] = 0;
		for(int i = head[v]; i; i = a[i].next) {
			if(dis[a[i].to] > dis[v] + a[i].value) {
				dis[a[i].to] = dis[v] + a[i].value;
				if(!visit[a[i].to]) {
					q[++tail] = a[i].to;
					visit[a[i].to] = 1;
				}
			}
		}
	}
}
int main() {
	int x, y, z;
	cin >> n >> m >> s >> t;
	for(int i = 0; i < m; i++) {
		cin >> x >> y >> z;
		add(x,y,z);
	}
	spfa();
	cout << dis[t] <<endl;
	return 0;
}

DFS

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
const int inf = 0x3f3f3f3f;
struct edge {
	int next,to,value;
}a[maxn];
int n, m, s, t, dis[maxn],head[maxn],cnt = 1;
void add(int x,int y,int z) {
	a[cnt].to = y;
	a[cnt].value = z;
	a[cnt].next = head[x];
	head[x] = cnt++;
}
void spfa(int pos) {
	for(int i = head[pos]; i; i = a[i].next) {
		if(dis[a[i].to] > dis[pos] + a[i].value) {
			dis[a[i].to] = dis[pos] + a[i].value;
			spfa(a[i].to);
		}
	}
}
int main() {
	int x, y, z;
	cin >> n >> m >> s >> t;
	for(int i = 0; i < n; i++) {
		cin >> x >> y >> z;
		add(x,y,z);
	}
	for(int i = 1; i <= n; i++)	dis[i] = inf;
	dis[s] = 0;
	spfa(s);
	cout << dis[t] <<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/lifehappy/p/12601194.html