C/C++ 最短路径-Floyd算法 (路径的保存和输出)

一、最短路径

  • 最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。
  • 算法具体的形式包括:
    • 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。
    • 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
    • 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
    • 全局最短路径问题 - 求图中所有的最短路径。

二、Floyd算法(弗洛伊德算法)

算法步骤

将vi到vj的最短路径长度初始化,即D[i][j]=G.arcs[i][j],然后进行n次比较和更新。

  • 在vi和vj间加入顶点v1,比较(vi, vj)和(vi, v1, vj)的路径长度,取其中较短者作为vi到vj的中间顶点序号不大于0的最短路径。
  • 在vi和vj间加入顶点v1,得到(vi, … , v1) 和(v1, … ,vj),其中(vi, … , v1) 是v1到vj的且中间顶点的序号不大于0的最短路径,(v1,··,v)是v到v的且中间顶点的序号不大于0的最短路径,这两条路径已在上一步中求出。比较(vi, … , v1, … , vj)与上一步求出的vi到vj的中间顶点序号不大于0的最短路径,取其中较短者作为vi到vj的中间顶点序号不大于1的最短路径。
  • 依次类推,在vi和vj间加入顶点v1,若(vi, … , vk)和(vk, … ,vj)分别是从vi到vk以和从vk到vj的中间顶点的序号不大于k-1的最短路径,则将(vi, … , vk , ··· , vj)和已经得到的从vi到vj且中间顶点序号不大于k-1的最短路径相比较,其长度较短者便是从vi到vj的中间顶点的序号不大于k的最短路径。这样,经过n次比较后,最后求得的必是从vi到vj的最短路径。
  • 按此方法,可以同时求得各对顶点间的最短路径。

三、举一个栗子(1076 : Floyd-Warshall

Description

暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。

Input

  • 第一行四个数为n,m,n表示顶点个数,m表示边的条数。
  • 接下来m行,每一行有三个数t1、t2 和t3,表示顶点t1到顶点t2的路程是t3。请注意这些t1->t2是单向的。

Output

输出一个n*n的矩阵,第n行第n列表示定点n到n的距离。每一行两个数间由空格隔开

Sample Input

5 8
1 2 2
2 3 3
3 4 4
4 5 5
5 3 3
3 1 4
2 5 7
1 5 10

Sample Output

0 2 5 9 9
7 0 3 7 7
4 6 0 4 9
12 14 8 0 5
7 9 3 7 0

code

详见注释

#include <iostream>
#define Max 503
#define INF 0xcffff

using namespace std;

typedef struct AMGraph {						//定义邻接矩阵
	int vex, arc;
	int arcs[Max][Max];
};

int dis[Max][Max], path[Max][Max];				//dis存最短路程,path保存路径

void Floyd(AMGraph &G)							//弗洛伊德算法
{
	for (int i = 1; i <= G.vex; i++)			//初始化dis和path
		for (int j = 1; j <= G.vex; j++)
		{
			dis[i][j] = G.arcs[i][j];
			if (dis[i][j] != INF&&i != j) path[i][j] = i;
			else path[i][j] = -1;
		}
			
	/***************Floyd核心算法****************/
	for(int k=1; k<=G.vex; k++)					//遍历每个点
		for(int i=1; i<=G.vex; i++)				//遍历每条边
			for (int j = 1; j <= G.vex; j++)
			{
				if (dis[i][k] + dis[k][j] < dis[i][j]) {	
					dis[i][j] = dis[i][k] + dis[k][j];			//松弛操作
					path[i][j] = k;								//保存j前驱结点k
				}
			}
}

void find(int x, int y)							//输出路径
{
	if (path[x][y] == x) return ;					//如果x,y之间无节点,则结束查找
	int t = path[x][y];								//如果x,y之间具有节点t,则查找t, y
	cout << "->" << t;
	find(t, y);
	return;
}

void putin(AMGraph &G)							//输入图
{
	int u, v, w;
	
	cin >> G.vex >> G.arc;

	for (int i = 1; i <= G.vex; i++)			//初始化邻接矩阵
		for (int j = 1; j <= G.vex; j++)
		{
			if (i == j) G.arcs[i][j] = 0;
			else G.arcs[i][j] = INF;
		}

	for (int i = 0; i < G.arc; i++)
	{
		cin >> u >> v >> w;
		G.arcs[u][v] = w;
//		G.arcs[v][u] = w;
	}
}

void putout(AMGraph &G)							//输出最短路矩阵、起点v1到各点的最短距离和路径
{
	//cout << "最短路矩阵为:\n";
	for (int i = 1; i <= G.vex; i++)
	{
		for (int j = 1; j < G.vex; j++)
			cout << path[i][j] << " ";
		cout << path[i][G.vex] << endl;
	}
	for (int i = 2; i <= G.vex; i++)			
	{
		cout << "从初始点 v1 到 v" << i << "的最短路径路程为:" << dis[1][i] << endl;
		cout << "路径为: 1";
		find(1, i);
		cout << "->" << i << endl;
	}
}

int main()
{
	AMGraph G;
	putin(G);
	Floyd(G);
	putout(G);
	return 0;
}

运行截图

去注释打印路径结果

Alt


蒟蒻一只,欢迎指正

  • 22
    点赞
  • 155
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值