图论——路径寻找问题(弗洛伊德算法和Dijkstra算法)

路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解。

图的存储方式:邻接矩阵、邻接表、十字链表、邻接多重表等。这里介绍邻接矩阵。

参见百度百科:

邻接矩阵是表示顶点之间相邻关系的矩阵。

设G=(V,E)是一个有向图,其逻辑结构分为两部分,V和E集合。因为我们可以用一个二维数组存放顶点与顶点的权值,数组下标表示顶点。

如:

给定一个图:

 

则该图的邻接矩阵为:

 

1

2

3

4

1

0

2

6

4

2

INF

0

3

INF

3

7

INF

0

1

4

5

INF

12

0

 什么是最短路?

假如我们要从2走到4,如图,我们有两种路径:

第一种:2——>3——>4      总权值(路径)为:4

第二种:2——>3——>1——>4  走权值(路径)为:14

所以第一种为最短路;

弗洛伊德算法(适用多源最短路径问题):

因为两个顶点之间不一定有直接连通且权值最小的路,所以往往要经过其顶点的中转而到达目标顶点。

比如:

当我们要求所有的顶点都必须经过顶点一时再求其最短路,我们就需要比较e[i][1]+e[1][i]与e[i][j]大小;

void solve()
{
	for(int i=1;i<=V;i++)
		for(int j=1;j<=V;j++)
			e[i][j]=min(e[i][1]+e[1][j],e[i][j]);
}

之后我们的邻接矩阵变为:

可以发现,有些顶点之间的权值变小了,比如说原本3——>2之间的权值为极大值,也就是3!——>2,现在3通过1中转至2,权值缩小为9,,所以弗洛伊德算法就是遍历整个图,保存每个顶点之间的最短路,时间复杂度o(n^3)

所以求整个图的最短路代码为:

void Floyd_Warshall()
{
	for(int k=1;k<=V;k++)
		for(int i=1;i<=V;i++)
			for(int j=1;j<=V;j++)
				e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}

Dijkstra算法(单源最短路径问题

迪杰斯特拉算法是计算一个点(注意是某一个顶点)其他所有点的最短路径,特点是采用广度优先搜索的思想,以起始点为中心,向外层层扩散,直到扩散到终点。

算法思路:

1.通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。

2.此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。

3.初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是”起点s到该顶点的路径”。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。

操作步骤:

1.首先指定一个起始点s;

2.定义一个二维数组e[MAXN][MAXN]和两个一维数组dis[MAXN],book[MAXN],e的作用保存图的信息,dis的作用是存储起始点到其他点的最短路径,book的作用是判断某个点是否被遍历过。

3.首先给dis数组赋值,设起始点为s,则dis[s]=0,因为到自己的距离为0,其他的,若起始点与该顶点之间有边,则赋值为该边(权值)大小,否则赋值为INF;

4.再book数组中选择一个距离顶点s最近的顶点u,存入dis数组中,然后遍历所有点u能直接到达的点(设为s1),并对每一条边进行判断,如果dis[s1]>dis[u]+e[u][s1],则dis[s1]=dis[u]+e[u][s1],一直重复此操作,直到所有点遍历完毕;

#define MAXN 60
#define INF 999999
int e[MAXN][MAXN];
bool book[MAXN]={false};
int dis[MAXN];
int V,E;
void prepare()
{
	//对邻接矩阵初始化 
	for(int i=0;i<MAXN;i++)
		for(int j=0;j<MAXN;j++)
			if(i==j)
				e[i][j]=0;
			else
				e[i][j]=INF;
	//输入
	int a,b,c;
	cin >> V >> E;
	for(int i=0;i<E;i++)
	{
		cin >> a >> b >> c;
		e[a][b]=c;		//从a到b的权值为c 
	} 
	
	//初始化dis数组
	for(int i=1;i<=V;i++)
		dis[i]=e[1][i];
		
	book[1]=true; 
}
void Dijkstra()
{
	int u;
	int minn=INF;
	for(int i=1;i<=V-1;i++)
	{
		minn=INF;
		//找距离1号最近的顶点 
		for(int j=1;j<=V;j++)
		{
			if(book[j]==false&&dis[j]<minn)
			{
				minn=dis[j];
				u=j;
			}
		}
		book[u]=true;
		//松弛 
		for(int k=1;k<=V;k++)
		{
			if(e[u][k]<INF)
				dis[k]=min(dis[k],dis[u]+e[u][k]);
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值