图结构练习——最短路径

Dijkstra算法

定义:迪克斯特拉算法,是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法
同时这个有权图中不能出现负边。

这里解决单源最短路径问题需要先了解Dijkstra算法,所以这里先把个人对于这个算法的理解附上,再解决最短路问题。

图结构练习——最短路径_第1张图片
以这个图为例,从1号走到7号的最短路径明显是从1到4再到7,这条路径的权值最小。从代码角度就要利用Dijkstra算法来解释。
图结构练习——最短路径_第2张图片
因为是从1号开始走,所以把所有与1号直接相连点,都记录下来,从1无法直接走到的点,其权值都暂时标记为无穷大。这时发现能走的点当中,权值最小的路径是走到2号,这个时候就走到2号,并把2号标记为走过。
图结构练习——最短路径_第3张图片
这个时候,再检索,与1号和2号直接相连的所有点,不是直接相连的的权值暂时都记为无穷。这是5号、3号、4号都是直接相连的,走到这些点的对应权值总和是4、2、3。这里说一下5这个点,到5号权值之所以是4,是因为把2号当成中转站了,也就是1->2->5这么走的。 这里又开始选权值最小的路径,那当然是选1->3这条路,走到3号并把3号标记为走过。
图结构练习——最短路径_第4张图片
把2号和3号当成中转站,开始检索与1、2、3号直接相连的点,并选出权值最小的路径、并刷新到每个点的权值,(无法直接到点,到那个点的路径权值暂时标记为无穷,意味着暂时走不到) 到5、6、3号的路径权值分别为4、8、3,所以到4号。
图结构练习——最短路径_第5张图片
来到4号,再重复上述操作,到5、6、7的路径权值分别为4、8、7。所以选7号,走过去。
图结构练习——最短路径_第6张图片
此时,已经到了目标点7号。但是,所有的点还没走完,所以接着走。
到了这里相信大家都知道该怎么做了,就是照着葫芦画瓢,把走过的路径当成中转站,检索所有可以直接走过去的点、计算出到那个点的权值、选出路径权值最小的一条,去走。最后又开始,调整到每个点的路径权值(因为走了一个点,就又多了一个中转站)
图结构练习——最短路径_第7张图片
最后,图会呈现这样的状态,而到每个点的路径权值都计算出来了,也记录了到每个点的路径最小权值。
总结一下就是,先检索每个能直接相连的点 (与中转站相连也算直接相连) ,并选出路径权值最小的那个点,走过去,标记那个点走过了,接着把那个点当成中转站,调整一下到每个点的路径权值,无法直接走到的点的路径权值就记为无穷大(意思就是暂时还走不到那个点),如此反复,直到所有点都走过了为止。
因为每次选的都是路径权值最小的,所以到目标点的路径权值一定是最小的。
这里把走过的点当成中转站,是一种理解,也可以把走过的点看成一个整体,当成一个大的点,也可以当成把那个点吞了之类的,怎么好想怎么来吧。

图结构练习——最短路径

Description
给定一个带权无向图,求节点1到节点n的最短路径。
Input
输入包含多组数据,格式如下。
第一行包括两个整数n m,代表节点个数和边的个数。(n<=100)
剩下m行每行3个正整数a b c,代表节点a和节点b之间有一条边,权值为c。
Output
每组输出占一行,仅输出从1到n的最短路径权值。(保证最短路径存在)
Sample
Input

3 2
1 2 1
1 3 1
1 0
Output
1
0

#include<bits/stdc++.h>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
int mp[111][111]; //图 
bool vis[111]; //标记是否走过 
int point[111]; //用来到某个点需要的权值 
void dijkstra(int n)
{
     
	int i,j,k,min,u;
	for(i=1;i<=n;i++) //因为是从1开始 
	{
     
		point[i]=mp[1][i];//先把与1相连的所有的点的权值,都记录下来 
	}                       //不相连的那些点记录进去的是无穷,但不影响后面的判断,因为是找最小的,所以无穷不影响判断 
	vis[1]=1; //1点标记为走过了 
	for(i=1;i<n;i++)//进行n-1次查询 
	{
     
		min=inf;
		for(j=1;j<=n;j++)
		{
     
			if(min>point[j]&&!vis[j]) //那个点没被走过的情况下,开始找最小的权值,并把那个点记录下来 
			{
     
				min=point[j];
				u=j;
			}
		}
		vis[u]=1;//找到的那个点,标记走过了 
		for(k=1;k<=n;k++) //开始调整到n个点的权值 
		{
     
			if(!vis[k]&&mp[u][k]<inf&&point[k]>mp[u][k]+point[u]) //在那个点没被走的情况下,从当前点能走到那个点的情况(废话,能走才会走过去) 
			{
                                                     //把当前记录的权值,与加入新点后走到那个点所需要的权值进行比较,记录小的那个 
				point[k]=mp[u][k]+point[u];
			}
		}
	}
	printf("%d\n",point[n]); //最后输出走到n点需要的总权值 
 } 
 int main()
 {
     
 	int n,m;
 	while(~scanf("%d%d",&n,&m))
 	{
     
 		memset(vis,0,sizeof(vis));//初始化 
 		memset(mp,inf,sizeof(mp)); //先把图中到所有点的步数都记为无穷 
 		memset(point,inf,sizeof(point));
 		for(int i=1;i<=n;i++) //当前点到当前点的步数记为0 
 		{
     
 			mp[i][i]=0;
		}
		while(m--)//开始读入图 
		{
     
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			if(mp[x][y]>z)//这里是为了防止路径覆盖 
			{
                 //比如说同一条路重复了2次但是路径数不一样,那肯定是记录小的那个 
				mp[x][y]=mp[y][x]=z;
			}
		}
		dijkstra(n); //把终点传过去 
	 }
	 return 0;
 }

这里的代码也是参考了学校里的学长和csdn的大佬。
如果你有任何建议或者批评和补充,请留言指出,不胜感激。

你可能感兴趣的:(笔记,数据结构,图论,数据结构)