图论——最短路(一)

概念:

    求图上一点到另一点的最短距离。

算法:

    一:Floyd(弗洛伊德) 

        Floyd算法可以求出图上任意一点到另一点的最短距离。

        思想:通过三重循环,用两重循环来枚举任意两点,一重循环枚举中节点,如果第一个点到中点+另一点到中点的距离<这两点先前的距离,则这两点的距离=第一个点到中点+另一点到中点的距离。

        代码如下:

inline void Floyd() {
	for (int k=1;k<=n;k++) //枚举中结点,i、j表示另两点 
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				if (a[i][k]+a[k][j]<a[i][j]) //如果第一个点到中点+另一点到中点的距离<这两点先前的距离
					a[i][j]=a[i][k]+a[k][j]; //更新两点间的距离 
}

        但是!Floyd的时间复杂度非常高,也非常稳定,为O(n^3)。只能在n<=500的情况下使用。

        Floyd可以无视负权,但不能判断负权回路,这就是接下来介绍的Dijkstra算法所不能及的了。

    二:Dijkstra(迪杰斯特拉)

        Dijkstra算法是求单元点到图中除单元点外任何一点的最短距离。

        这个算法只能用来求一个点到其他点的距离,时间复杂度为O(n^2)。如果将每个点都做一遍Dijkstra,那它的效果也就相当于是Floyd。但是它相对于Floyd的好处就是可以O(n^2)求单元点到其他点的距离,这个Floyd就做不到了。

        思想:利用贪心的思想,求出在该点连接的边中,找到权值最小的一条,那么这条边就是到这个点到原点的最短路(因为不可能有其他边更新的边会比这条边短了)。再通过这个点,去更新其他与改点所连的点,如果原点到被该点连接的点的距离<原点到改点+改点连接的边的距离,则更新被连点到原点的距离,最后将该点赋值为真,避免下次再重复更新(每个点最初到原点的距离为正无穷,原点到原点除外)。

        代码如下://这里采用邻接矩阵的写法,有兴趣可以去参考其他用邻接表的写法

inline void dijkstra(int st) {
	memset(f,0,sizeof(f)); //fi表示单元点到i的最短距离是否被确定
	memset(dis,10,sizeof(dis)); //memset 10表示正无穷 
	for (int i=1;i<=n;i++) dis[i]=a[i][st]; //dis数组表示单元点到i的距离
	f[st]=1,dis[st]=0; //前者表示单元点到单元点的距离唯一,后者表示单元点到单元点的距离为0 
	for (int i=1;i<=n;i++) {
		int minn=2147483647,k=0; //k表示为未被使用过的连接原点的点的编号(有点拗口)
		for (int j=1;j<=n;j++) 
			if (!f[j] && dis[j]<minn) minn=dis[j],k=j; //!f[j]表示未被使用过
		if (k==0) return ; //连接的点中全被使用过
		f[k]=1; //标记为已被使用过
		for (int j=1;j<=n;j++) 
			if (!f[j] && dis[k]+a[k][j]<dis[j]) dis[j]=dis[k]+a[k][j]; //三角形迭代,更新最短距离 
	} 
} 
        这里要特别注意一点就是赋值成正无穷,不仅dis数组,a数组读进来之前也要,不然就会输出0。

        Dijkstra算法还可以通过堆优化,可以将时间复杂度优化到O(n log(n)),有兴趣可以去查阅资料,这里就不再说明(主要是因为我也不会)

        但是Dijkstra在有负权回路的情况下,会怎么样,自行想象。在这种情况下,就要用到Bellman-Ford或者SPFA,具体用法见下文。


猜你喜欢

转载自blog.csdn.net/chenkainan1023/article/details/80147785