Summary of shortest path algorithm

Posted by podja on Mon, 20 Sep 2021 08:21:44 +0200

Summary of shortest path algorithm

1, Catalogue

1.Floyd (n^3) n: number of points
2. Dijikstra (n ^ 2 - > mlogn) n: number of points m: number of edges (complexity is not understood)
3. Bellman Ford (nm) n: number of points m: number of sides
4.spfa(Km) K: constant of about 2 m: number of sides
5.Johnson (nmlogm) (do not understand complexity)

2, Implemented code

1.floyd full source shortest circuit (negative weight side can be solved, but negative weight circuit cannot be solved)

Core of the code: during the process from point i to point j, find out whether there is point K (k! = i & & K! = j) as the transfer point, so that the shortest circuit between point i and point j can be updated, so as to complete the code.

Note: k should be placed in the outermost loop (essentially related to dynamic gauge state transfer), otherwise some magical data points will make mistakes!!!

#include<bits/stdc++.h>
using namespace std;
#define N 200 / / basically the maximum data range

int f[N][N];//The shortest distance between two points 
int main(){
	int n, m;//n is the number of points and m is the number of edges
	scanf("%d%d", &n, &m);
	memset(f, 0x3f, sizeof(f));//Initialize to infinity
	for(int i = 1; i <= n; ++ i)
		f[i][i] = 0;//The distance from self to self is initialized to 0
	for(int j = 1; j <= m; ++ m){
		scanf("%d%d%d", &x, &y, &z);
		if(z < f[x][y]) f[x][y] = f[y][x] = z;
	}
	for(int k = 1; k <= n; ++ k)//k must circulate in the outermost layer as a state of dynamic transfer
		for(int i = 1; i <= n; ++ i)
			for(int j = 1; j <= n; ++ j)
				if(i != k && i != j && k != j && f[x][k] + f[k][y] < f[x][y]) f[x][y] = f[y][x] = f[x][k] + f[k][y];
	f[a][b]mean a,b Shortest distance between two points
	return 0;
}

2.dijikstra single source shortest circuit (negative weight edge cannot be solved)

The core of the code: if the relaxation operation can be realized, add the points to the priority queue, take out the points with the shortest distance from the starting point for expansion each time, and ensure that each point traverses all its adjacent points only once.

Here, the code is directly optimized with heap, because the dijikstra that is not optimized has little significance (high time complexity)

Note: when pressing into the queue, the opposite number of blank holder weights should be. (the default is large root heap)

#include<bits/stdc++.h>
using namespace std;
const int N = 1050;//Basic is the maximum data range 
int dis[N];
bool vis[N];
vector<pair <int , int> >vec[N];
priority_queue<pair <int , int> >que;//Big root pile 
void dijk(int x){
	que.push(make_pair(0, x));
	dis[x] = 0;
	while(!que.empty()){
		int u = que.top().second;
		que.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		int len = vec[u].size();
		for(int i = 0; i < len; ++ i){
			int v = vec[u][i].first;
			int w = vec[u][i].second;
			if(dis[u] + w < dis[v]){
				dis[v] = dis[u] + w;
				que.push(make_pair(-dis[v], v));//Press
			}
		}
	}
	return ;
}
int main(){
	int x, y, v;
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++ i){
		scanf("%d%d%d", &x, &y, &v);
		vec[x].push_back(make_pair(y, v));
		vec[y].push_back(make_pair(x, v));
	}
	int center;
	scanf("%d", &center);
	memset(dis, 63, sizeof(dis));
	dijikstra(center);//Find the shortest path of each point starting from the center and store it in the dis array 
	printf("%d", dis[Target point]);
	return 0;
}

3. Bellman Ford single source shortest path (can solve the negative weight edge, can not solve the negative ring, but can judge the negative ring)

Code core: run n cycles, and the distance between the first and last points of m edges each time. If it can be updated, it will be updated in time.

#include<bits/stdc++.h>
using namespace std;
#define N 3050

struct Edge{
	int u, v;
	int w;
}edge[N];//Structure array is used to store edges 
int dis[N];
int main(){
	int U, V;//start and finish 
	scanf("%d%d", &U, &V);
	memset(dis, 63, sizeof(dis));
	dis[U] = 0;//initialization 
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++ i)
		scanf("%d%d%d", &edge[i].u, &edge[i].v, edge[i].w);
	for(int i = 1; i <= n; ++ i)
		for(int j = 1; j <= m; ++ j){
			if(dis[edge[j].u] + edge[j].w < dis[edge[j].v])
				dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
			if(dis[edge[j].v] + edge[j].w < dis[edge[j].u])
				dis[edge[j].u] = dis[edge[j].v] + edge[j].w;
		}
	for(int j = 1; j <= m; ++ j)
		if(dis[edge[j].u] + edge[j].w < dis[edge[j].v] || dis[edge[j].v] + edge[j].w < dis[edge[j].u])
			printf("There is a negative ring!!!")
	printf("%d", dis[V]);
	return 0;
}

4.spfa finds the shortest path of a single source (it can solve the negative weight edge, but it can not solve the negative loop, so it can judge the negative loop)

Code core: with the bellman Ford algorithm optimized by queue, the redundant loop is omitted, and the operation efficiency is greatly high.

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;

int n, m;
vector<pair<int, int> >vec[N];
queue<pair<int, int> >que;

int dis[N], d[N];//d[N] is the number of steps required to take the shortest path to reach a certain point 
bool vis[N];
int spfa(int s)
{
	memset(dis, 63, sizeof(dis));
	dis[s] = 0;
	vis[s] = 1;
	que.push(make_pair(0, s));
	while(!que.empty())
	{
		int u = que.front().second;
		vis[u] = 0;
		que.pop();
		for(int i = 0; i < vec[u].size(); ++i){
			int v = vec[u][i].first;
			int w = vec[u][i].second;
			if(dis[u] + w < dis[v]){
				dis[v] = dis[u] + w;
				d[v] = d[u] + 1;
				if(d[v] >= n) return 1;//If there is no negative loop, take the shortest path to reach a point and use n-1 edges at most 
				if(!vis[v]){
					vis[v] = 1;
					que.push(make_pair(dis[v], v));
				}	
			}
		}
	}
	return 0;
}
int main()
{
	scanf("%d%d", &n, &m);	
	int x, y, z;
	for(int i = 1; i <= m; ++i){
		scanf("%d%d%d", &x, &y, &z);
		vec[x].push_back(make_pair(y, z));
		vec[y].push_back(make_pair(x, z));
	}
	int U, V;//U: Start V: end 
	scanf("%d%d", &U, &V);
	int judge = spfa(U);
	if(judge == 1) puts("There is a negative ring!!!")
	else	printf("%d", dis[V]);
	return 0;
}

5.johnson all source shortest circuit (negative weight edge can be solved, negative loop can be judged if negative loop cannot be solved)

Code key: the combination of Bellman Ford and dijikstra is better than spfa algorithm for n times in a large data range.

Reprint [Los Angeles Daily #242] Johnson's learning notes on all source shortest path algorithm
Link: https://zhuanlan.zhihu.com/p/99802850

Exercise: P5905 [template] Johnson all source shortest circuit
Link: https://www.luogu.com.cn/problem/P5905

It is suggested to read the notes first and then do the problem. Here is the solution to this problem

#include<bits/stdc++.h>
using namespace std;
#define N 3005

int h[N], vis[N];
long long dis[N];
struct Edge{
	int u, v;
	int w;
}edge[N * 2];//Structure array is used to store edges
vector<pair <int , int> >vec[N];
priority_queue<pair <long long , int> >que;//Big root pile 
void dijikstra(int x){
	memset(vis, 0, sizeof(vis));
	que.push(make_pair(0, x));
	dis[x] = 0;
	while(!que.empty()){
		int u = que.top().second;
		que.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		int len = vec[u].size();
		for(int i = 0; i < len; ++ i){
			int v = vec[u][i].first;
			int w = vec[u][i].second;
			if(dis[u] + w < dis[v]){
				dis[v] = dis[u] + w;
				que.push(make_pair(-dis[v], v));
			}
		}
	}
	return ;
} 
int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	int x, y, z;
	for(int i = 1; i <= m; ++ i){
		scanf("%d%d%d", &x, &y, &z);
		edge[i].u = x;
		edge[i].v = y;
		edge[i].w = z;
	}
	for(int i = 0; i <= n; ++ i)
		for(int j = 1; j <= m; ++ j){
			if(h[edge[j].u] + edge[j].w < h[edge[j].v])
				h[edge[j].v] = h[edge[j].u] + edge[j].w;			
		}
	for(int j = 1; j <= m; ++ j)
		if(h[edge[j].u] + edge[j].w < h[edge[j].v]){
			puts("-1");
			return 0;
		}
	for(int i = 1; i <= m; ++ i)
		vec[edge[i].u].push_back(make_pair(edge[i].v, edge[i].w + h[edge[i].u] - h[edge[i].v]));
	for(int i = 1; i <= n; ++ i)
		dis[i] = 1e9;
	for(int i = 1; i <= n; ++ i){
		dijikstra(i);
		long long tot = 0;
		for(int j = 1; j <= n; ++ j){
			if(dis[j] == 1e9) tot += j * 1e9;
			else tot += j * (dis[j] + h[j] - h[i]);
			dis[j] = 1e9;
		}
		printf("%lld\n", tot);
	}
	return 0;
}

The above is the summary of the shortest path algorithm. There must still be some errors. You are welcome to criticize and correct. It's not easy to sort out. If it brings you some help, I hope you can like forwarding, thank you!!!

Topics: Algorithm