最短路之 路径记录 dijkstra + floyd + SPFA 【小笔记】

引子:我们在求解最短路的时候,有的时候需要输出路径,这里就介绍下路径记录及输出的一些方法。


为了更好理解,提出一个问题:

给出n个车站,m条单向路线以及 每条路线的信息即 起点终点和对应的花费(n与m同时为0表示结束),然后给出一系列的起点s和终点e(s和e同时为-1表示结束),让你求出最小花费并输出路径(假定给出的两点均是可达的)。


(一) dijkstra算法实现:


思路:用一个数组path[]记录前驱顶点,找到最短路后,从终点倒向追踪,直到找到起点为止。为了方便我这里用了栈来记录,不用栈用一个新的数组记录也可以的。


需要注意的有一点:在初始化path[]数组时,在i(1 <= i <= n)不等于起点s的前提下,对那些可以由s直达的点i,要有path[i] = s。

代码就是这样:pre[i] = dist[i]!=INF&&i!=s ? s : -1;



代码实现:

#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500]; 
int pre[500];//记录当前顶点的 前一个顶点 
int dist[500];
bool vis[500];
int n, m;
void init()
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			Map[i][j] = i==j ? 0 : INF;
		}
	}
} 
void getMap()
{
	int a, b, c;
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		if(Map[a][b] > c)
		Map[a][b] = c;
	} 
}
void dijkstra(int s, int e)
{
	int Min, next;
	for(int i = 1; i <= n; i++)
	{
		dist[i] = Map[s][i];
		vis[i] = false;
		pre[i] = dist[i]!=INF&&i!=s ? s : -1;//初始化要注意 
	} 
	vis[s] = true;
	for(int i = 2; i <= n; i++)
	{
		Min = INF;
		for(int j = 1; j <= n; j++)
		{
			if(!vis[j] && dist[j] < Min)
			{
				Min = dist[j];
				next = j;
			}
		} 
		vis[next] = true;
		for(int j = 1; j <= n; j++)
		{
			if(!vis[j] && dist[j] > dist[next] + Map[next][j])
			{
				dist[j] = dist[next] + Map[next][j];
				pre[j] = next;//记录 
			}
		}
	}
} 
int main()
{
	int s, e;
	while(scanf("%d %d", &n, &m), n||m)
	{
		init();
		getMap();
		while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
		{
			dijkstra(s, e);
			if(s == e)
			{
				printf("从%d到%d的最优路线 : %d\n", s, e, s);
				printf("最小花费 : %d\n", 0);
				continue;
			}
			int now = pre[e];
			stack<int> path;
			path.push(e);
			while(1)
			{
				path.push(now);
				if(now == s)
				break;
				now = pre[now];
			}
			printf("从%d到%d的最优路线 : %d", s, e, s);
			while(!path.empty())
			{
				printf("-->%d", path.top());
				path.pop();
			}
			printf("\n");
			printf("最小花费 : %d\n", dist[e]);
		}
	}
	return 0;
}

(二) floyd算法实现:

思路:用一个二维数组path[][]来记录。这里有两种方法,1 用path[ i ][ j ]记录 j 的前驱顶点 ;2 用path[ i ][ j ]记录 i 的后面的点。


提醒:需要注意的是path的初始化


代码实现第一种方法:

#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500]; 
int pre[500][500];//记录当前顶点的 前一个顶点 
int n, m;
void init()
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			Map[i][j] = i==j ? 0 : INF;
			pre[i][j] = i;//初始化 
		}
	}
} 
void getMap()
{
	int a, b, c;
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		if(Map[a][b] > c)
		Map[a][b] = c;
	} 
}
void floyd()
{
	int i, j, k;
	for(k = 1; k <= n; k++)
	{
		for(i = 1; i <= n; i++)
		{
			for(j = 1; j <= n; j++)
			{
				if(Map[i][j] > Map[i][k] + Map[k][j])
				{
					Map[i][j] = Map[i][k] + Map[k][j];
					pre[i][j] = pre[k][j];//记录 
				}
			} 
		} 
	} 
}
int main()
{
	int s, e;
	while(scanf("%d %d", &n, &m), n||m)
	{
		init();
		getMap();
		floyd();
		while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
		{
			if(s == e)
			{
				printf("从%d到%d的最优路线 : %d\n", s, e, s);
				printf("最小花费 : %d\n", 0);
				continue;
			}
			int now = pre[s][e];
			stack<int> path;//记录路径 
			path.push(e);
			while(1)
			{
				path.push(now);
				if(now == s)
				break;
				now = pre[s][now];
			}
			printf("从%d到%d的最优路线 : %d", s, e, s);
			path.pop();
			while(!path.empty())
			{
				printf("-->%d", path.top());
				path.pop();
			}
			printf("\n");
			printf("最小花费 : %d\n", Map[s][e]);
		}
	}
	return 0;
}

代码实现第二种方法:这个没有用栈 建议掌握好这个方法

#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500]; 
int pre[500][500];//记录当前顶点的 前一个顶点 
int n, m;
void init()
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			Map[i][j] = i==j ? 0 : INF;
			pre[i][j] = j;//初始化 
		}
	}
} 
void getMap()
{
	int a, b, c;
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		if(Map[a][b] > c)
		Map[a][b] = c;
	} 
}
void floyd()
{
	int i, j, k;
	for(k = 1; k <= n; k++)
	{
		for(i = 1; i <= n; i++)
		{
			for(j = 1; j <= n; j++)
			{
				if(Map[i][j] > Map[i][k] + Map[k][j])
				{
					Map[i][j] = Map[i][k] + Map[k][j];
					pre[i][j] = pre[i][k];//记录 
				}
			} 
		} 
	} 
}
int main()
{
	int s, e;
	while(scanf("%d %d", &n, &m), n||m)
	{
		init();
		getMap();
		floyd();
		while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
		{
			if(s == e)
			{
				printf("从%d到%d的最优路线 : %d\n", s, e, s);
				printf("最小花费 : %d\n", 0);
				continue;
			}
			printf("从%d到%d的最优路线 : %d", s, e, s);
			int now = pre[s][e];
			while(1)
			{
				printf("-->%d", now);
				if(now == e)
				break;
				now = pre[now][e];
			}
			printf("\n");
			printf("最小花费 : %d\n", Map[s][e]);
		}
	}
	return 0;
}

(三)SPFA算法实现:


思路:同dijkstra。。。详看代码


代码实现:

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
struct Edge
{
	int from, to, val, next;
}edge[250000];
int dist[500];
int head[500], top;
bool vis[500];
int pre[500];//记录前驱顶点
int n, m;
void init()
{
	top = 0;
	memset(head, -1, sizeof(head));
	memset(pre, -1, sizeof(pre));
}
void addEdge(int u, int v, int w)
{
	Edge E = {u, v, w, head[u]};
	edge[top] = E;
	head[u] = top++;
}
void getMap()
{
	int a, b, c;
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		addEdge(a, b, c);
	}
}
void SPFA(int s, int e)
{
	queue<int> Q;
	for(int i = 1; i <= n; i++)
	dist[i] = i==s ? 0 : INF; 
	memset(vis, false, sizeof(vis));
	vis[s] = true;
	Q.push(s);
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		vis[u] = false;
		for(int i = head[u]; i != -1; i = edge[i].next)
		{
			Edge E = edge[i];
			if(dist[E.to] > dist[u] + E.val)
			{
				dist[E.to] = dist[u] + E.val;
				pre[E.to] = u;
				if(!vis[E.to])
				{
					vis[E.to] = true;
				    Q.push(E.to);
				} 
			}
		}
	}
}
int main()
{
	int s, e;
	while(scanf("%d%d", &n, &m), n||m)
	{
		init();
		getMap();
		while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
		{
			SPFA(s, e);
			if(s == e)
			{
				printf("从%d到%d最优路线 : %d\n", s, e, s);
				printf("最小花费 : %d\n", 0);
				continue;
			}
			stack<int> path;
			path.push(e);
			int now = pre[e];
			while(1)
			{
				path.push(now);
				if(now == s)
				break;
				now = pre[now];
			}
			printf("从%d到%d最优路线 : %d", s, e, s);
			path.pop();
			while(!path.empty())
			{
				printf("-->%d", path.top());
				path.pop(); 
			}
			printf("\n");
			printf("最小花费 : %d\n", dist[e]);
		}
	}
	return 0;
} 




    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/chenzhenyu123456/article/details/46670451
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞